agentleFS
CodesWhatCodesWhat/careerrat/.agents/skills/track-outcomes/SKILL.md

track-outcomes

Skill4 starsChanged 11 days agoMITView on GitHub

Record application outcomes, execute status transitions in tracker.json, append durable patterns to role-family learning files, check reevaluation thresholds, and write back any mid-flow gate changes stated by the user.

The file

Show its rules and text

We split this skill into the rules it gives an agent

22 rules. About 0 are common, 8 are distinctive, and 14 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 14

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Mode detection: run careerrat data status.

    line 18
  2. Very specific:

    If candidate/targeting.yml#reevaluation is absent, use defaults: rejection_total: 7, rejection_per_family: 3.

    line 29
  3. Very specific:

    Write Contract in AGENTS.md — do not hand-null those fields.

    line 85
  4. Very specific:

    After saving a rejection, withdrawal, cut or closed application, its convenience folder belongs under …

    line 120
  5. Very specific:

    If a communications[] record exists for this job, it must be updated for this same outcome.

    line 133
  6. Very specific:

    If app.followUp.draft is set and step 1 wasn't itself a comm mark-sent call, clear it too: careerrat data app set-fields <app-id> --data …

    line 170
  7. Very specific:

    If either reports errors, the DB row itself is wrong — fix it with another careerrat data app set-fields / comm upsert call (never …

    line 201
  8. Distinctive:

    Do not continue until both exit clean.

    line 204
  9. Distinctive:

    After editing, run BOTH validators — they check different things and neither replaces the other (see RULES):

    line 215
  10. Distinctive:

    If either reports errors, fix the JSON before proceeding.

    line 223
  11. Very specific:

    DB workspace: every careerrat data <verb> call in STEP 2/2b already re-exported workspace/tracker.json + workspace/activity.jsonl (Data …

    line 233
  12. Distinctive:

    Legacy workspace (no DB): run:

    line 243
  13. Distinctive:

    If the outcome creates or extends a follow-up cadence (e.g., interview scheduled, offer pending response, ghosted application to chase), …

    line 251
  14. Distinctive:

    Keep entries evidence-linked — only record what was actually observed or stated.

    line 315
  15. Very specific:

    Write the entry body to a temp file (e.g.

    line 317
  16. Distinctive:

    If the preview looks correct, commit the entry:

    line 325
  17. Very specific:

    If reevaluation.due is true, hand off to reevaluate-strategy and report:

    line 347
  18. Very specific:

    If reevaluation.due is false but advanced.byFamily shows a strong concentration of advances in one family, note it and hand off to …

    line 352
  19. Distinctive:

    If the user states any new constraint during the outcome flow ("never apply to X again", "add this as a cut signal", "cap this company at 1 …

    line 356
  20. Very specific:

    Commit workspace/tracker.json, any updated candidate/learnings/<family>.md, and any gate file changes with a Conventional Commits message:

    line 370
  21. Very specific:

    Use email-comms for drafting follow-ups or replies.

    line 385
  22. Very specific:

    Do not check reevaluation thresholds from prose in AGENTS.md — always read tracker.json#analytics.reevaluation (refreshed automatically by …

    line 386

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

track-outcomes

Runs under AGENTS.md. These contracts bind without being restated here: Privacy Invariant (current_base never outbound), Honesty Firewall, Placeholder/Bracket Ban, Gate Write-back, Domain-Neutral Rule, Browser Automation Contract, Activity Pulse logging, Tracker verify+snapshot, and Sent-Clears-Draft. Inline reminders at point-of-use are intentional; standalone restatements point back to the relevant AGENTS.md section. Bare candidate/, workspace/, config/, and .internal/ paths below are symbolic; resolve them per AGENTS.md's Path Resolution rule.

STEP 0 — Classify the incoming outcome

Accept one of: rejection, interview invite, offer received, ghosted/stale, status update, blocker removal, manual note, or explicit gate change ("never apply to X again", "add Y as a cut signal").

Identify the target application by company + role title (or id if known). Read the matching row from workspace/tracker.json (applications array). If no matching row exists, confirm with the user before creating a new entry.

STEP 1 — Read gate files

Mode detection: run careerrat data status. Exit 0 → DB workspace — every write step below gives the careerrat data <verb> command (Data Write Contract, AGENTS.md). Nonzero exit → legacy workspace (no DB yet) — every write step below gives the existing direct JSON-edit instructions, unchanged.

Read in order:

  1. candidate/targeting.yml — role_buckets, role_families (if present), cut_signals, excluded_companies, and reevaluation thresholds (rejection_total, rejection_per_family).
  2. Application limits — DB mode: careerrat data candidate get --json (application-limits.companies[]); legacy mode: candidate/application-limits.yml.
  3. workspace/tracker.json — the full applications array for current status, fitScore, channel, mode, appliedAt, note, and conversations[].

If candidate/targeting.yml#reevaluation is absent, use defaults: rejection_total: 7, rejection_per_family: 3.

STEP 2 — Execute the status transition

Canonical status vocabulary

StatusArrayBoard visibilityMeaning
sourcedsourced[]activeDiscovered, not yet evaluated
reviewed-holdsourced[]activePassed the evaluate-job gate; ready to pursue
cutsourced[]archivedFailed the gate (cut_signals, exclusions, comp below floor). Off the active board but kept in data, recoverable. Do not delete.
closedsourced[]archivedPosting gone / expired before evaluation. Off the active board.
manual-applyapplications[]activeAuto-apply could not complete (Cloudflare/CAPTCHA human-check, ATS account or password reset required, Ashby/Workday exercise, ATS spam-rejection, or any step needing the human). Human must finish applying. ACTIVE — stays surfaced on the board. Record the specific reason + the apply URL in note. Never call this "blocked".
awaitingapplications[]activeSubmitted; waiting for response
interviewapplications[]activeInterview stage
offerapplications[]activeOffer received
rejectedapplications[]active/archiveClosed with rejection
withdrawnapplications[]archivedCandidate chose to withdraw mid-process (comp gap, competing offer, culture read, role-scope mismatch, or proactive exit). Distinct from a market rejection — the candidate acted, the market did not close. (Pre-application pruning — role cut/hold/skipped — also lands here via the STAGE_RULES keyword map, but that is scanner-only behavior; this status in applications[] always means a voluntary mid-process candidate exit.)

Archived statuses (cut, closed, withdrawn) are off the active board but stay in the data. The dashboard triage banner counts reviewed-hold + manual-apply and prompts the user to review them with the agent.

manual-apply is always active, never archived. Once the human finishes applying, transition to awaiting.

Permitted transitions

FromEventTo
sourcedgate pass (evaluate-job KEEP/REVIEW)reviewed-hold
sourcedgate cut (evaluate-job CUT)cut (archived)
sourcedposting goneclosed (archived)
reviewed-holdsubmitted successfullyawaiting
reviewed-holdauto-apply blocked, human neededmanual-apply
reviewed-holduser decides to passcut (archived)
manual-applyhuman finishes applyingawaiting
manual-applyuser decides to passwithdrawn (archived)
awaitinginterview inviteinterview
awaitingrejectionrejected
interviewfinal outcomeoffer / rejected
interviewcandidate withdrawswithdrawn (archived)
offeraccepted/declinedclose the row with a note
anyuser withdrawswithdrawn (archived)

Apply the transition:

  • DB workspace: run

    careerrat data app set-status <id> <to> --note "<statusNote text>" \
      [--follow-up-due <iso>] [--clear-interview|--no-clear-interview]

    One call sets status and statusNote (via --note) and followUp.dueAt (via --follow-up-due), bumps the stamp, refreshes analytics, and logs the activity event — all in one transaction. On round completion (leaving interview), it also nulls nextInterviewAt/interviewNote/interviewAt (the last one only when no next round is booked) automatically per the Data Write Contract in AGENTS.md — do not hand-null those fields; use --clear-interview/--no-clear-interview only to override the auto-detection for the edge case of advancing rounds while status stays interview. For appliedAt on a new submission, or any other field not covered by set-status, follow with careerrat data app set-fields <id> --data '{"<field>": "<value>"}' (shallow merge one level; not outcome-changing, so it does not re-refresh analytics — the preceding set-status call already did).

  • Legacy workspace (no DB): edit workspace/tracker.json directly —
    • Set status to the new value.
    • Set the appropriate date field: appliedAt if this is a new submission.
    • On round completion (transitioning OUT of interview → rejected, offer, or next-round advance): in the same write, null out app.nextInterviewAt, null out app.interviewNote, and null out app.interviewAt if this was the last scheduled round. (If the candidate is advancing to a next round, schedule-meeting will write the new nextInterviewAt when that round is booked — track-outcomes always clears the just-completed round's values regardless.)

Either way, write the outcome to typed, single-topic fields per the Tracker Content Register in AGENTS.md — no superlatives, no editorializing, evidence only. The old "note OR conversations[]" choice is replaced by this routing:

What you heardWrite toDB-mode command
Stage filtered / rejection / live stateapp.statusNote (≤120 chars) + an activity eventcovered by app set-status --note above
Comp / band signalapp.compNote (≤140 chars)app set-fields <id> --data '{"compNote":"<text>"}'
Interview scheduledapp.interviewNote (≤60 chars) + a conversations[] entry (round kind follows the canonical Round Vocabulary in AGENTS.md — type, never number; final only when the process truly ends there)app schedule-interview <id> --at <iso> --round "<kind>" --note "<text>"
Process intel (next rounds, stakeholders)conversations[].processNote (≤160 chars)app set-fields <id> --data '{"conversations":[...]}' — conversations replaces wholesale (array field), so read the current row first and patch the one entry
Coaching / objectionsconversations[].learnings[] (labeled, ≤5)same as above — app set-fields with the full patched conversations array
Keyword / screening fitcandidate/learnings/<family>.mdunchanged in both modes — see STEP 5

app.note is internal-only (search text + a ≤60-char submission one-liner) and is never rendered on a card — do not use it as an outcome dumping ground (DB mode: app set-fields <id> --data '{"note":"<text>"}'). Use expected_base / target_base / minimum_base for any comp — never write current_base into any tracker field or note under any circumstances.

Keep the saved job folder current

After saving a rejection, withdrawal, cut or closed application, its convenience folder belongs under ~/Documents/careerrat/_archive/<Company>/job-<application-id>/. Reopening the application restores ~/Documents/careerrat/<Company>/job-<application-id>/. Move the whole job folder, including previous rounds, rather than deleting files or moving a company folder that may contain another live role. Keep canonical workspace paths, tracker records, conversations and evidence where they are. The DB status verbs do this automatically after committing the outcome; read any reported folder error before saying cleanup succeeded. For a legacy JSON workspace, perform the same move after the tracker write is verified. Never overwrite an existing archive destination or guess ownership of older company-root exports.

STEP 2b — Comm record write-back

If a communications[] record exists for this job, it must be updated for this same outcome. A change that updates app.* and leaves the comm record untouched leaves a ghost CTA on every comm-derived render surface (Next Steps card, Focus card, Action Queue).

Set comm.status:

  • Rejection, candidate withdrawal, role closed → closed
  • Interview invite, offer received → waiting (next round or decision pending)
  • Ghosted/stale being closed out → closed
  • Blocker removed, data-completion form submitted, additional-info provided → waiting

Also clear comm.nextActionDue (→ null) and comm.draft (→ null, and app.followUp.draft too if present) — the expected event has occurred and the draft that backed it is gone — then append a note recording what happened.

  • DB workspace: comm.mjs exposes upsert (full-row replace), append-message, and mark-sent (status → waiting + draft clear, meant for an actual outbound send) — there is no single partial-patch verb yet, so compose two calls, back-to-back in the same turn as STEP 2's app set-status (there is no combined app+comm transaction, so "same write" becomes "same turn — never leave the second call for later"):

    1. Patch the comm row locally (read the current shape from workspace/tracker.json#communications[], apply the status / nextActionDue: null / draft: null rules above), then persist the whole row:

      careerrat data comm upsert --data '<patched full comm row JSON>'
    2. Append the outcome note:

      careerrat data comm append-message <comm-id> --data '{"direction":"note","at":"<ISO timestamp>","summary":"<one-liner, e.g. Rejection received — recorded by track-outcomes>"}'

    If app.followUp.draft is set and step 1 wasn't itself a comm mark-sent call, clear it too: careerrat data app set-fields <app-id> --data '{"followUp": {"draft": null}}'. Neither call refreshes analytics (not outcome-changing) — STEP 2's app set-status already did.

  • Legacy workspace (no DB): edit workspace/tracker.json directly, in the same write as the app-row change from STEP 2:
    1. Set comm.status, comm.nextActionDue = null, comm.draft = null (and app.followUp.draft = null if present).
    2. Append a note to comm.messages[]:

      { "direction": "note", "at": "<ISO timestamp>", "summary": "<one-liner: what was recorded, e.g. 'Rejection received — recorded by track-outcomes'>" }

D.E.Shaw / Avature / data-completion case: if the completion event is a submitted additional-info or data-completion form that was tracked in a communications[] thread (identified by subject line or recruiter name), treat that thread as the comm record above — flip it to waiting, null its nextActionDue, clear its draft, and append the outbound-completion note (DB mode: the same comm upsert + comm append-message pair).

STEP 3 — Persist and validate

  • DB workspace: STEP 2 and STEP 2b already persisted the state — each careerrat data <verb> call is its own atomic transaction, and each auto-exports workspace/tracker.json + workspace/activity.jsonl afterward (Data Write Contract, AGENTS.md). There is no single combined app+comm transaction, so the completeness guarantee is on the sequence of calls, not one write — run the STEP 2 and STEP 2b calls back-to-back, never deferring the comm call. Validate what landed:

    careerrat data verify          # re-exports, then domain integrity: status/score/modes/channels/dupes
    careerrat tracker --verify     # JSON shape/structure vs tracker.schema.json (data verify doesn't run this)

    If either reports errors, the DB row itself is wrong — fix it with another careerrat data app set-fields / comm upsert call (never hand-edit tracker.json, it is a regenerated file and the next export overwrites it). Do not continue until both exit clean.

    Analytics: app set-status (and sourced promote) already refreshed tracker.json#analytics as part of their own transaction — no separate call needed. If STEP 2 wrote only via app set-fields (e.g. a manual note with no status change), run careerrat data analytics-refresh explicitly so STEP 6 reads a current reevaluation gate.

  • Legacy workspace (no DB): edit workspace/tracker.json directly (the tracker CLI has no mutation subcommands — JSON edits are the only write path).

    Single-write requirement: all app.* and comm.* field changes from STEP 2 and STEP 2b must land in ONE tracker.json write. This includes the interview-round-completion nulls (nextInterviewAt, interviewNote, interviewAt) when transitioning out of an interview round. A partial write that updates the app row and leaves the comm record or interview datetime fields untouched is non-compliant — it leaves ghost CTAs on Next Steps, Focus card, and Action Queue.

    After editing, run BOTH validators — they check different things and neither replaces the other (see RULES):

    careerrat tracker --verify   # JSON shape/structure vs tracker.schema.json
    npm run verify:tracker              # domain integrity: status/score/modes/channels/dupes

    If either reports errors, fix the JSON before proceeding. Do not continue to the next step until BOTH exit clean.

    Refresh the analytics block (required — this is an outcome-changing write). After both validators pass and BEFORE snapshotting, recompute and persist tracker.json#analytics so STEP 6 reads a current reevaluation gate. Skipping this leaves the block one run stale, so a threshold crossed by the rejection you just logged is invisible to STEP 6 and the reevaluate-strategy handoff fires late or not at all:

    careerrat analytics --write   # recompute tracker.json#analytics: rejection/advance counts + reevaluation.due/dueReasons

STEP 4 — Snapshot tracker state

  • DB workspace: every careerrat data <verb> call in STEP 2/2b already re-exported workspace/tracker.json + workspace/activity.jsonl (Data Write Contract, AGENTS.md). If careerrat tracker-dev is running, its fs.watch on tracker.json already picked this up and live-reloaded the open page — nothing further to do. For a recovery checkpoint, still run:

    careerrat tracker
  • Legacy workspace (no DB): run:

    careerrat tracker

Confirm the status change appears correctly in the live app. If the app output looks wrong, diagnose before proceeding.

If the outcome creates or extends a follow-up cadence (e.g., interview scheduled, offer pending response, ghosted application to chase), also run (a read, unaffected by DB vs legacy mode):

careerrat tracker --followups

Review any follow-ups now due and hand off to email-comms if a draft is needed.

Ghost CTA check: verify that no communications[] record for this job still shows comm.nextActionDue in the past, a non-null comm.draft, or comm.status in a stale state after a terminal app outcome. If found, STEP 2b was missed — go back and apply the comm write-back before continuing (DB mode: re-run the comm upsert patch).

Log the outcome to the Activity Pulse feed (the dashboard's live timeline — see Activity Pulse in AGENTS.md), picking the type by what happened:

  • DB workspace: STEP 2's app set-status call already auto-logged one generic status_change event (and STEP 2b's comm upsert/comm append-message calls each logged their own). For the richer, outcome-specific type below, log an additional event — this verb only logs, it never bumps the stamp, and there is no --write/dry-run flag, every call is a real write:

    careerrat data activity append --data '{"type":"interview","actor":"world","title":"Interview stage — <Company>","summary":"<stage / detail>","refs":{"applicationId":"<application id>","company":"<Company>"}}'

    Swap type/title/summary/actor per the categories below (offer, status_change, failure with "needsUser":true for a blocker).

  • Legacy workspace (no DB):

    # interview / screen / onsite advance:
    careerrat activity append --type interview --actor world \
      --title "Interview stage — <Company>" --summary "<stage / detail>" \
      --company "<Company>" --app-id <application id> --write
    
    # offer:
    careerrat activity append --type offer --actor world \
      --title "Offer — <Company>" --summary "<detail>" \
      --company "<Company>" --app-id <application id> --write
    
    # rejection / closed:
    careerrat activity append --type status_change --actor world \
      --title "Closed — <Company>" --summary "<reason>" \
      --company "<Company>" --app-id <application id> --write
    
    # candidate withdrawal (neutral tone — candidate exited, market did not close):
    careerrat activity append --type status_change --actor agent \
      --title "Withdrew — <Company>" --summary "<reason: comp gap / competing offer / culture read / role-scope mismatch / proactive exit>" \
      --company "<Company>" --app-id <application id> --write
    
    # blocker needing the user:
    careerrat activity append --type failure --actor world --needs-user \
      --title "Blocked — <Company>" --summary "<what's blocking>" \
      --company "<Company>" --app-id <application id> --write

STEP 5 — Append to role-family learning file

The learnings helper resolves the family slug automatically from candidate/targeting.yml (role_families → role_buckets → neutral slug); no manual derivation is needed.

Compose the entry body as markdown, capturing only durable signal:

  • On rejection: stage where filtered, objection or gap heard, comp signal if disclosed, keywords or framing that did not land, channel.
  • On candidate withdrawal: reason the candidate chose to exit (comp gap, competing offer, culture read, role-scope mismatch, timeline mismatch, or proactive de-prioritization). This is strategy signal — record it to track which conditions drive voluntary exits so patterns can inform future targeting.
  • On advance/offer: winning positioning or phrasings, keywords that landed, comp signal, channel that converted.
  • On ghosted/stale: funnel stage last known, channel, elapsed time.

Keep entries evidence-linked — only record what was actually observed or stated. Do not invent lessons.

Write the entry body to a temp file (e.g. /tmp/learning-body.md), then:

  1. Dry-run (preview + lint):

    careerrat learnings append "<role title>" --title "<short label>" --body-file /tmp/learning-body.md
  2. If the preview looks correct, commit the entry:

    careerrat learnings append "<role title>" --title "<short label>" --body-file /tmp/learning-body.md --write

The helper creates candidate/learnings/ and the family file on first --write. A missing family file is normal — the helper handles it silently.

STEP 6 — Check reevaluation thresholds

The analytics block in tracker.json#analytics.reevaluation already applies the threshold comparison — it is refreshed automatically by app set-status/sourced promote in a DB workspace, or by careerrat analytics --write as part of the Tracker Write Contract in a legacy workspace (STEP 3/4). STEP 6 reads; it does not recompute.

Read tracker.json#analytics.reevaluation:

  • reevaluation.due — true if any rejection threshold is crossed since the last recorded strategy review.
  • reevaluation.dueReasons — human-readable strings explaining which thresholds fired (e.g. "total 8>=7", "family:fde 3>=3").
  • reevaluation.sinceLastReview.rejectionTotal — rejection delta since the last strategy review (not all-time total).
  • reevaluation.sinceLastReview.rejectionByFamily — per-family rejection delta since the last strategy review.
  • reevaluation.thresholds — the resolved threshold values (rejectionTotal, rejectionPerFamily) in effect when the block was last computed.

Also read tracker.json#analytics.advanced.byFamily — a cluster of advances or offers concentrated in one family or channel is a signal to double-down on that targeting even when no rejection threshold fired.

If reevaluation.due is true, hand off to reevaluate-strategy and report:

  1. The dueReasons strings (which thresholds fired).
  2. The sinceLastReview counts (delta totals and per-family breakdown).

If reevaluation.due is false but advanced.byFamily shows a strong concentration of advances in one family, note it and hand off to reevaluate-strategy for a double-down review.

STEP 7 — Write back mid-flow gate changes

If the user states any new constraint during the outcome flow ("never apply to X again", "add this as a cut signal", "cap this company at 1 app"), persist it through the owning DB-aware command immediately:

  • Exclusion → careerrat gate exclude-company "<Company>" --write
  • Cut signal → careerrat gate cut-signal "<signal>" --write
  • Keep signal → careerrat gate keep-signal "<signal>" --write
  • Per-company cap or cooldown → write careerrat data candidate limits upsert --data '<json row>' in DB mode; legacy mode writes candidate/application-limits.yml. It applies directly and is logged with an Undo.
  • Comp floor or anchor change → careerrat gate comp-floor <N> --write or careerrat gate comp-target <N> --write. Raising one, or setting it for the first time, writes directly. Lowering or clearing one needs the user's yes first, then add --confirm.

Friction rule: knowledge and targeting writes (signals, exclusions, caps, re-ranks) apply directly through the same writers and are logged with an Undo. Confirm-first stays for lowering a comp floor or target, loosening an honesty boundary, and any strategy change backed by fewer than 3 data points (log it as a suggestion). After writing, echo the CLI confirmation.

A stated gate must never live only in chat. Do not hardcode it into skill prose.

STEP 8 — Commit

Commit workspace/tracker.json, any updated candidate/learnings/<family>.md, and any gate file changes with a Conventional Commits message:

feat(tracker): record <outcome-type> for <Company> — <Role>

Include the role family and threshold status in the commit body if a reevaluation threshold was tripped.


RULES

  • Tracker mutations: DB workspace (careerrat data status exits 0) — go through careerrat data <verb> (Data Write Contract, AGENTS.md); never hand-edit tracker.json/activity.jsonl, they are regenerated files. Legacy workspace (no DB) — direct JSON edits + two complementary validation checks + careerrat tracker snapshot: run careerrat tracker --verify (validates JSON shape/structure against config/tracker.schema.json — required keys, field presence) AND npm run verify:tracker (validates domain integrity — status recognizability, score range 0–100, modes, channels, duplicate company-role pairs). Both must pass; they check different things and neither replaces the other; the legacy tracker CLI itself is read-only (no mutation subcommands). Never fabricate a mutation subcommand beyond what careerrat data --help documents.
  • Never write current_base into any tracker field, note, conversations entry, or learning file. Use expected_base, target_base, or minimum_base only (the learnings helper enforces this).
  • Notes must be factual. No superlatives, no invented lessons, no editorializing.
  • Use email-comms for drafting follow-ups or replies. This skill records the outcome; it does not draft outbound text.
  • Do not check reevaluation thresholds from prose in AGENTS.md — always read tracker.json#analytics.reevaluation (refreshed automatically by app set-status/sourced promote in a DB workspace, or by careerrat analytics --write in a legacy workspace). The block applies the threshold comparison; the agent reads reevaluation.due and reevaluation.dueReasons, it does not recompute.
  • Role-family taxonomy is driven by candidate/targeting.yml (role_families or role_buckets); classifyRoleFamily in outcome-analysis.mjs accepts a targeting arg and prefers candidate-supplied families over the built-in tech slugs, which apply only when no candidate config is present. analyze-outcomes.mjs wires targeting through, so non-tech candidates get correct family files.
  • Reevaluation threshold fields (reevaluation.rejection_total, reevaluation.rejection_per_family) live in candidate/targeting.yml (schema'd) and are resolved into tracker.json#analytics.reevaluation.thresholds by buildReevaluationAnalytics(). The threshold comparison is done by the analytics block — do not manually read the YAML values and branch on them in STEP 6. Read the block, trust reevaluation.due.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.