The right skill for the next step, powered by Jev from TypeSafe.ai.
A standalone Rust CLI that puts TypeSafe.ai's Jev at the center of skill selection: Jev evaluates your agent's live context, compares the available skills, and estimates which ones fit the next step. SkillRanker supplies the session integration, local safeguards, and inspectable feedback around it.
A TypeSafe API key is required to use SkillRanker's ranking system. Sign up at the TypeSafe console to get your own key.
sr demo --case useful # Inspect an offline fixture before connecting a session
sr rank --allow-network # Rank skills for the selected session
sr hook claude # Run the Claude Code prompt-hook integration
sr tui # Inspect rankings in an inline terminal display
TL;DR
The problem. A large skill library gives an agent plenty of procedures to choose from, but choosing is itself a task. Similar descriptions obscure useful distinctions. A skill that helped at the start of a conversation can be irrelevant three turns later. Loading a plausible but unsuitable skill consumes context and can redirect otherwise sensible work.
The solution. SkillRanker (sr) combines the recent conversation, current
request, workspace signals, and the selected harness's visible skill inventory.
Jev from TypeSafe.ai is the key enabler of the system. It first compares the
candidates broadly, then reads richer excerpts from a shortlist and evaluates
whether each one fits. Both comparisons include a
real “none of these” option. The result is advisory: the agent follows the user's
instructions and decides what to consult.
For libraries with more than 254 eligible skills, Quill from FrankenSearch narrows the candidates locally before Jev evaluates them. Smaller rosters reach Jev in full. Explicit skill requests resolve locally before either stage.
SkillRanker does not include a local model or a substitute inference provider. The ranking workflow requires your own TypeSafe account and API key. Local retrieval prepares the candidates; Jev supplies the evaluations that make the recommendations possible.
Why sr?
| Need | What SkillRanker provides |
|---|---|
| Evaluate meaning and task fit | Jev's typed Choice and Noul evaluations from TypeSafe.ai power both ranking passes |
| Choose for the current step | Exact session identity, the newest prompt, recent tool evidence, and project signals |
| Suggest something the agent can load | Harness-aware visibility, override resolution, stable skill identities, and content revalidation |
| Respect an explicit request | Locally resolve a requested skill before probabilistic retrieval or ranking |
| Search a large library | Quill lexical prefiltering from FrankenSearch, admitting up to 254 skills plus a none option to each Choice |
| Separate similar skills | Detailed reranking with bounded descriptions and body excerpts |
| Recognize when no skill fits | Relevance gates, per-candidate fit checks, and sentinel-based abstention |
| Understand a missing suggestion | --why-not traces where a candidate was excluded, with thresholds and concrete recovery hints |
| Reproduce a surprising result | Opt-in case capture and offline replay compare compatible local policies without another Jev call |
| Get started without sharing a session | Offline fixture demos and a readiness report identify the next setup step |
| Keep the agent moving | A failed hook recommendation produces a quiet, non-blocking fallback |
| Control interruptions | Silent ordinary abstentions and scoped, expiring snoozes preserve explicit skill requests |
| Bound repeated expense | Optional shared HTTP-attempt allowances and a provider circuit breaker cover concurrent local sessions |
| Review what happens | Usefulness, interruptions, attempts, and cost share a report with explicit label coverage |
| Evaluate within a budget | Offline replay, explicit live-request caps, and reproducible samples with recorded selection probabilities |
| Assess recommendation harm | Controlled comparisons, uncertainty bounds, and optional monitoring across repeated evaluations |
| Control disclosure | Network opt-in, field-level disclosure receipts, a minimal context profile, and separate persistence controls |
The approach builds on the TypeSafe skill-suggestion recipe. SkillRanker adds session identity, harness visibility, bounded execution, and a local evaluation loop. The comprehensive plan explains the full design and acceptance criteria.
Quick Example
# See a labeled fixture result without a key, network, or private session.
sr demo --case useful
# Inspect local configuration and the available adapters.
sr doctor --json
sr doctor --config
sr capabilities --json
# Inspect visible, shadowed, and excluded skill records.
sr roster --json
# Preview the redacted wide-pass request without network or persistence effects.
sr rank --context scratch/context.json --dry-run
# Evaluate an explicitly selected conversation.
sr rank --context scratch/context.json --allow-network --json
# Find where an expected candidate was excluded; this adds no inference calls.
sr rank --context scratch/context.json --allow-network --why-not SKILL_ID --explain
# Preview the Claude hook settings change, then apply it.
sr install-hook claude
sr install-hook claude --apply
# Review observations without treating adoption as proof of usefulness.
sr stats --since 7d --by-skill
# Replay a labeled evaluation artifact without making network requests.
sr eval --dataset scratch/evaluation.json --explain
# Inspect description quality and suspected coverage gaps locally.
sr doctor --descriptions
sr gaps
Design Philosophy
- Choose for the next action. The current request matters, as do the recent failure, the task context, and the instructions already loaded.
- Resolve authority locally. The user decides what is required or excluded. The harness determines what can be loaded. A model answer cannot change either.
- Separate preference from applicability. Winning a comparison is not enough. A recommendation must survive fit, visibility, loaded-state, and none-option checks.
- Keep evidence inspectable. Preserve provider estimates and local arithmetic.
--explainexposes computations without inventing model-generated reasons. - Separate adoption from usefulness. Observing a load is useful telemetry. Learning a better policy requires independently judged examples and a holdout.
- Bound each evaluation. Input, discovery, subprocesses, networking, retries, persistence, and cleanup consume one ranking deadline. Batch evaluation also has explicit request and total-runtime limits.
- Stay standalone. Discovery, parsing, redaction, retrieval, and feedback live
in
sr. No skill-manager service or private database is required.
How It Compares
These are workflow choices, not benchmark rankings.
| Approach | Input to selection | Strength | Tradeoff |
|---|---|---|---|
| Manual selection | Your knowledge of the task and library | Direct control without a ranking service | Requires remembering each skill's coverage |
| Keyword search | A query over names and descriptions | Cheap local discovery | Synonyms and adjacent procedures can be hard to distinguish |
| Load every skill | The full library's instructions | Makes every procedure available immediately | Consumes context regardless of relevance |
| SkillRanker | Exact session, visible roster, and explicit constraints | Evaluates candidates and can abstain | Fresh Jev evaluations require authorized network access |
SkillRanker recommends procedures. It does not execute skills, grant permissions, or override the agent's governing instructions.
Installation
From source
Build the sr binary with the repository's pinned Rust toolchain and lockfile:
git clone https://github.com/Dicklesworthstone/skillranker.git
cd skillranker
cargo install --locked --path . --bin sr
For a checkout-local binary:
cargo build --locked --release --bin sr
./target/release/sr --help
Runtime setup
Sign up for TypeSafe.ai, then create your own API key in the console. A TypeSafe API key is required to use SkillRanker's ranking system. Jev is the evaluation engine for the entire ranking workflow. Every user supplies their own credential; SkillRanker does not distribute a shared key.
Set TYPESAFE_API_KEY through your shell or secret manager. The
environment example lists the service settings. For a local
checkout, create .env from the example only if it does not already exist and
restrict access with chmod 600 .env before entering your own API key.
The .env file is ignored by Git. Export its values into the process environment
before running sr or starting an agent whose hooks need the key:
# Run from your checkout, after filling in your own trusted .env file.
set +x
set -a
. ./.env
set +a
Treat .env as a local shell configuration file and source only content you
trust. Keep the key out of shell history, logs, and tracked files. Credentials
alone do not enable remote transmission.
| Component | Role |
|---|---|
| TypeSafe API key | Authenticates fresh Jev evaluations |
| Network opt-in | --allow-network for a run, or network.enabled in trusted user configuration |
| Visible skill inventory | Harness-resolved skills or an explicit roster file |
| Session input | Claude hook, normalized context, supported native transcript, or optional cass export |
| cass | Optional archive discovery and access across coding-agent formats |
sr does not require ms, a local inference server, or an embedding model.
The primary local platform scope is Linux and macOS. Consult
sr capabilities --json for the adapters, events, and optional features in a build.
Quick Start
- Try an offline fixture. Run
sr demo --case useful, then trynone,explicit, orunavailable. These labeled examples exercise the local pipeline without reading a private session or contacting Jev. Their output is non-actionable and does not establish live provider health. - Sign up and configure your own TypeSafe API key. Create an account and key
in the TypeSafe console, then export
TYPESAFE_API_KEYusing the runtime setup instructions. SkillRanker relies on Jev for its ranking evaluations. - Check the environment and roster. Run
sr doctor --json,sr capabilities --json, andsr roster --jsonin the agent's workspace. Confirm that candidates are loadable, not merely present somewhere on disk. - Choose the session. Supply
--context FILE,--transcript FILE --harness claude_code, or--session PATHfor cass. Automatic discovery must resolve one unambiguous session. - Preview and rank. Use
--dry-runto inspect the redacted wide payload and disclosure receipt, then--allow-network --jsonfor a fresh evaluation. - Prepare a recorded shadow trial. Initialize optional history with
sr ledger init, then explicitly setnetwork.enabled = truein trusted user configuration if you want live hook evaluations. The earlier--allow-networkflag authorized only that CLI run. Preview and applysr install-hook claude; shadow mode evaluates without injecting advice. Without a ready ledger, the hook can rank but cannot promise recorded trial evidence; without network consent, it cannot obtain fresh Jev answers. - Enable advisory output deliberately. Set
hook.mode = "advisory"in trusted user configuration after reviewing the integration and its behavior.
Readiness checks
sr doctor reports each prerequisite separately, names the next concrete step
for a failed check, and lists local commands that remain useful:
| Check | What it establishes |
|---|---|
| Input and roster | A usable source and loadable candidates are available |
| Credential | A key is present; this alone does not authenticate it |
| Network authorization | Trusted settings permit a live request |
| Transport | Untested or previously verified by a separately authorized, budgeted live check |
| Ledger | Optional history is ready or degraded; its absence need not block ranking |
| Hook and snoozes | Shadow/advisory mode and active scoped interruption controls |
Doctor runs locally by default and does not implicitly send a test request, install hooks, migrate storage, or change configuration. Demo uses bundled synthetic contexts and labeled synthetic or recorded responses without touching user configuration or state. Neither command supplies a replacement for Jev.
A previous transport check includes its time, scope, and runtime/endpoint/configuration identity. Incompatible changes invalidate it; a stored check does not establish current provider availability.
sr capabilities --json separates implemented adapters from tested harness
versions/features and unverified versions. Adapter conformance covers prompt
timing, branch identity, visibility, restrictions, compaction, load evidence, and
hook output, deadlines, and delivery. Native advice requires passing real-harness
evidence for every dimension on the installed version; protocol fixtures and
official schema documentation alone cannot authorize it. Cass remains an
explicit archive source. Native Codex, omp/pi, and Grok integrations each need
their own support record. Incompatible identity or visibility semantics disable
advice. The adapter contract defines these checks.
Command Reference
Bare sr is equivalent to sr rank: a table on a TTY, JSON otherwise. It does
not start a TUI. Source flags are mutually exclusive, and piped stdin is consumed
only by an explicit input mode. sr capabilities --json lists the commands,
schemas, and optional features available in the installed build.
Ranking and inspection
| Command | Purpose | Example |
|---|---|---|
sr demo --case CASE |
Inspect a labeled offline fixture | sr demo --case unavailable |
sr rank |
Rank the next step | sr rank --allow-network --json |
sr rank --context FILE |
Read normalized context; - means stdin |
sr rank --context scratch/context.json --dry-run |
sr rank --transcript FILE --harness NAME |
Read a supported native transcript | sr rank --transcript scratch/session.jsonl --harness claude_code --offline |
sr rank --session PATH |
Export an exact session through cass | sr rank --session scratch/session.jsonl --allow-network |
sr rank --why-not ID --explain |
Trace an expected candidate's exclusion | sr rank --allow-network --why-not SKILL_ID --explain |
sr rank --save-case FILE |
Explicitly capture a bounded redacted replay case | sr rank --allow-network --save-case scratch/case.json |
sr replay FILE |
Recompute a historical case offline | sr replay scratch/case.json --compare-policy scratch/candidate.toml |
sr hook claude |
Handle the Claude prompt-hook protocol | sr hook claude --shadow |
sr roster --json |
Inspect visibility, overrides, records, and exclusions | sr roster --json |
sr roster --snapshot FILE |
Explicitly export a bounded private roster manifest | sr roster --snapshot scratch/roster-snapshot.json |
sr roster --diff FILE |
Compare fresh discovery with a saved roster snapshot | sr roster --diff scratch/roster-snapshot.json |
sr doctor --json |
Inspect local configuration and readiness | sr doctor --json |
sr doctor --config |
Explain effective non-secret values and their sources | sr doctor --config |
sr capabilities --json |
Describe commands, schemas, features, limits, and exits | sr capabilities --json |
sr tui |
Open the inline viewer | sr tui |
Hooks, feedback, and analysis
| Command | Purpose | Example |
|---|---|---|
sr install-hook claude |
Preview a managed hook settings change | sr install-hook claude --apply |
sr uninstall-hook claude |
Preview removal of the managed entry | sr uninstall-hook claude --apply |
sr stats |
Report observation and operational metrics | sr stats --since 7d --by-skill |
sr observe |
Reconcile structured load events | sr observe --transcript scratch/session.jsonl --harness claude_code |
sr feedback |
Record an explicit usefulness judgment | sr feedback EVENT_ID --skill SKILL_ID --verdict useful |
sr feedback --instead ID |
Record a better alternative for an event | sr feedback EVENT_ID --skill SKILL_ID --instead ALTERNATIVE_ID |
sr snooze |
Preview a scoped temporary advisory mute | sr snooze EVENT_ID --skill SKILL_ID --for 30m |
sr budget |
Inspect or preview a shared HTTP-attempt allowance | sr budget --max-attempts 100 --window 1h |
sr eval |
Replay a labeled evaluation artifact offline by default | sr eval --dataset scratch/evaluation.json --explain |
sr calibrate |
Report a candidate threshold configuration | sr calibrate --evaluation scratch/report.json |
sr calibrate --rollback REVISION |
Preview restoration of managed policy fields | sr calibrate --rollback POLICY_REVISION |
sr doctor --descriptions |
Check description quality locally | sr doctor --descriptions |
sr gaps |
Report suspected coverage gaps | sr gaps |
sr ledger init |
Initialize local history explicitly | sr ledger init |
sr ledger migrate |
Preview a supported schema upgrade | sr ledger migrate --apply |
sr ledger prune |
Preview retention cleanup | sr ledger prune --before 2026-09-01 |
sr ledger clear |
Preview clearing local history | sr ledger clear |
--apply performs a previewed hook, snooze, budget, calibration, or ledger mutation.
Calibration consumes a labeled evaluation artifact; it does not silently change
project settings after a number of observed loads. Description audits use the network
only with an explicit online request and network authorization.
Evaluation controls
sr eval defaults to replay with zero network requests. A live run needs
--online, trusted network authorization, and an explicit --max-requests cap.
That cap counts HTTP attempts across the entire batch, including retries. Each
case also has its own ranking deadline; the batch stops scheduling work when a
request or runtime limit is reached and reports unfinished cases.
| Flag | Default | Meaning |
|---|---|---|
--dataset FILE |
Required | Versioned, consented evaluation data and compatible recorded responses for replay |
--online |
Off | Permit fresh Jev evaluations when network access is separately authorized |
--max-requests N |
Required for live runs | Maximum HTTP attempts across the batch, including retries |
--max-runtime-ms N |
600000 |
Overall batch deadline, in addition to per-case deadlines |
--sample-size N |
Full supplied frame | Select a bounded sample of task-family representatives |
--seed S |
Fresh recorded random seed when sampling | Deterministic diagnostic selection or reproduction of a recorded sample; a fixed seed alone is not probability-sampling evidence |
--explain |
Off | Include equations, substituted values, assumptions, and interpretation in the report |
# Reproduce a diagnostic selection; seed 42 alone supports no sampling guarantee.
sr eval --dataset scratch/evaluation.json --sample-size 100 --seed 42 --explain
# Draw and record a random sample, then authorize a bounded live evaluation.
sr eval --dataset scratch/evaluation.json --sample-size 100 \
--online --allow-network --max-requests 400 --max-runtime-ms 600000
Sampling does not grant network access or enlarge the request budget. Missing stage responses remain unevaluated in replay; they are never replaced with invented scores. See evaluation and sampling for the report's denominators and uncertainty rules.
Ranking controls
| Flag | Default | Meaning |
|---|---|---|
--messages N |
12 |
Recent logical messages |
--budget-chars N |
12000 |
Rendered context budget, including the latest request |
--top K |
5 |
Maximum eligible suggestions returned |
--shortlist M |
8 |
Real candidates admitted to the rerank |
--gate F |
0.30 |
Overall need threshold |
--fits F |
0.30 |
Minimum candidate fit |
--timeout-ms N |
3000 |
Whole one-shot ranking deadline; applied separately to each TUI/watch refresh |
--roster FILE |
Harness discovery | Replace discovery with an explicit inventory |
--require-skill ID |
None | Resolve an explicit required skill; repeatable |
--latest |
Off | Explicitly choose the newest discovered session |
--context-profile PROFILE |
standard |
Choose standard context or the bounded minimal disclosure profile |
--no-tools |
Off | Remove tool arguments and results from outgoing context |
--no-cache |
Off | Disable response-cache reads and writes |
--no-ledger |
Off | Disable all ledger and ingestion-cursor access; use transient evidence |
--no-persist |
Off | Disable all persistent state, including cache, cursors, and locks |
--offline |
Off | Guarantee zero network calls |
--allow-network |
Off | Authorize network evaluation for this invocation |
--explain |
Off | Include distributions, exclusions, truncation, and score contributions |
--why-not ID |
None | Trace a candidate from the current snapshot with --explain, without adding requests |
--save-case FILE |
Off | CLI-only, explicit capture for offline replay; cannot overwrite an existing file |
--dry-run |
Off | Preview the redacted request without network or persistence effects |
Sizes satisfy 1 ≤ K ≤ M ≤ 32; fewer available candidates is normal. Parsing
is strict, with documented aliases only. Invalid or conflicting privacy flags
produce an error rather than being silently corrected.
An explicit chunk-overflow experiment uses bounded groups and reduction rounds. It has separate request limits and availability in the capabilities contract; the normal overflow policy uses local prefiltering.
JSON output
The versioned output contract specifies decision, error, quality, trace, and non-actionable report schemas.
This illustrative result has two eligible candidates. The score arithmetic uses
w_fit = 1, with priors and phase weighting disabled; timing and usage are examples.
{
"schema_version": 1,
"event_id": "example-event-001",
"decision": "ranked",
"reason": "eligible-candidates",
"harness": "claude_code",
"context_quality": "complete",
"quality": {
"prompt_complete": true,
"task_anchor_known": true,
"history_windowed": true,
"attachments_omitted": false,
"source_gaps": false
},
"roster": {
"total": 2,
"eligible": 2,
"wide_candidates": 2,
"shortlist": 2,
"partial": false,
"retrieval": "full",
"provenance": {
"snapshot_id": "000000000000000000000000000000000000000000000000000000000000000a",
"policy_version": "ranking-v1",
"wide_set_id": "000000000000000000000000000000000000000000000000000000000000000b",
"rerank_set_id": "000000000000000000000000000000000000000000000000000000000000000c"
}
},
"needs_skill": 0.74,
"choice_confidence": 0.81,
"none_probability": 0.10,
"phase": "debugging",
"skills": [
{
"rank": 1,
"skill_id": "s_01",
"name": "rust-test-triage",
"invocation_name": "rust-test-triage",
"rank_score": 0.888889,
"rerank_probability": 0.60,
"wide_probability": 0.55,
"fits": 0.80,
"path": ".claude/skills/rust-test-triage/SKILL.md",
"content_hash": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"rank": 2,
"skill_id": "s_02",
"name": "rust-code-review",
"invocation_name": "rust-code-review",
"rank_score": 0.111111,
"rerank_probability": 0.30,
"wide_probability": 0.35,
"fits": 0.50,
"path": ".claude/skills/rust-code-review/SKILL.md",
"content_hash": "0000000000000000000000000000000000000000000000000000000000000002"
}
],
"omitted_rank_mass": 0.0,
"cache": {
"hit": false,
"wide_hit": false,
"rerank_hit": false,
"age_ms": null,
"stale": false
},
"model": {
"requested": "jev-latest",
"wide_returned": "jev-latest",
"rerank_returned": "jev-latest",
"immutable_revision": null
},
"usage": {
"requests": 2,
"http_attempts": 2,
"input_tokens": 6400,
"output_tokens": 480,
"unknown_usage_attempts": 0
},
"persistence": "recorded",
"warnings": [],
"warnings_omitted": 0,
"elapsed_ms": 720
}
| Decision | Meaning |
|---|---|
ranked |
Up to K eligible suggestions from a successful evaluation |
explicit |
Locally resolved user requests, with no invented model certainty |
abstain |
Valid input and policy produced no advisory recommendation |
unavailable |
An operational, input, privacy, or coverage problem prevented a decision |
Demo and replay use separately versioned, non-actionable envelopes around their synthetic or historical decisions. A demo's context is synthetic; any recorded provider response retains its own provenance. It remains demonstration evidence, not a live evaluation or a quality-gate result. Neither artifact uses the live hook output channel.
choice_confidence describes the rerank distribution. fits is a model estimate
of suitability. rank_score is a relative local score over eligible candidates.
They are different quantities. Top-K truncation preserves the original eligible
normalization and reports omitted mass. Fields from an unexecuted stage are
null, not fabricated zeros.
Cache hits and returned model identities are recorded separately for each stage.
When reranking is required, a wide-stage hit alone is not a complete offline
result. An alias such as
jev-latest does not identify an immutable model revision.
persistence: "recorded" means the ranking metadata was committed before output;
it does not mean the harness acknowledged or consumed the recommendation.
Quality metadata includes prompt_complete, task_anchor_known,
history_windowed, attachments_omitted, and source_gaps. These describe the
admitted input; context_quality: "complete" does not claim that the entire
conversation history was read. Warnings are bounded to 32 details plus an omitted
count. Live decision JSON and individual demo/replay/report summary envelopes
are capped at 2 MiB. The separate 256 MiB evaluation-artifact limit bounds a
streamed dataset or report with at most 10,000 case records; it does not enlarge
an individual output envelope. Roster listings and full-wide explanations
paginate against a fixed snapshot; a changed snapshot requires restarting.
Trace pages contain at most 128 entries and bind both snapshot and query identity.
Unevaluated entries keep operands and reasons null instead of inventing evidence.
Exit codes
| Code | Meaning |
|---|---|
0 |
Ranked, explicit, valid abstention, or another successfully completed command |
2 |
Invalid usage or configuration |
3 |
Missing or ambiguous session |
4 |
Provider, authentication, or network failure; request-admission refusal |
5 |
Empty/unusable roster, unresolved explicit request, or Quill retrieval failure |
6 |
Overall deadline exhausted |
7 |
Malformed, oversized, or unsupported input |
8 |
Network transmission disallowed |
9 |
Required storage or administrative mutation failed |
10 |
Invalid structured provider response |
11 |
No complete valid result under offline/cache-only constraints |
JSON errors include schema_version, decision: "unavailable", and an error
object with code, kebab-case kind, message, hint, and retryable.
Ordinary ranking can succeed with a storage warning; an explicit feedback write
cannot claim success when its required write failed.
Incomplete essential context and output-limit failures use exit 7, unresolved
explicit requests and Quill retrieval failures use 5, and superseded input uses 3.
Overall deadline exhaustion uses 6. retryable means a
fresh invocation with the same intended inputs may succeed; it does not grant
network access or relax a deadline.
Evaluation and replay reports distinguish execution from quality:
run_status is complete or partial; gate_status is passed, failed,
not-established, or not-applicable. Exit zero means the report was produced,
not that every comparison was replayable or a policy passed. Promotion requires
the intended complete cohort, compatible evidence, and explicit passed gates.
Fatal errors retain their nonzero exit code and available partial-work/usage data.
Each summary accounts for requested/completed cases and required/completed
stages. Partial, incompatible, empty, or synthetic evidence cannot carry a
passed gate. A completed report can faithfully describe a failed historical
request without treating that historical error as a failure to generate the report.
The dedicated hook maps recommendation failures to quiet exit-zero behavior so it never blocks the agent. CLI failures retain their meaningful exit codes.
Configuration
Ordinary settings resolve from lowest to highest priority:
built-in defaults
-> trusted user configuration
-> allowlisted workspace configuration
-> recognized SR_* environment variables
-> command-line flags
On Linux, user configuration falls back to ~/.config/sr/config.toml; project
configuration is .sr/config.toml at the workspace root. Other platforms use
native configuration directories.
Trusted user settings for an advisory hook include:
[network]
enabled = true
[hook]
mode = "advisory"
Without those choices, remote transmission is disabled and the hook runs in
shadow mode. --allow-network can authorize a single CLI evaluation.
| Variable | Purpose |
|---|---|
TYPESAFE_API_KEY |
TypeSafe bearer credential; never serialized or stored in project config |
TYPESAFE_ENDPOINT |
Trusted HTTPS base origin; sr appends /v1/systemone once |
SR_MODEL |
Requested model; default jev-latest |
SR_MESSAGES, SR_BUDGET_CHARS |
Context-volume limits: 1–12 messages and 1–12,000 Unicode scalar values |
SR_TOP, SR_SHORTLIST |
Output and rerank sizes, satisfying 1 ≤ top ≤ shortlist ≤ 32 |
SR_GATE, SR_FITS |
Finite thresholds in [0, 1] |
SR_TIMEOUT_MS |
Whole-invocation deadline, 201–60,000 ms |
Workspace configuration may tune bounded ranking values and exclusions. It
cannot authorize networking, change endpoints/proxies, supply credentials,
expand transcript access, disable redaction, or enable raw retention. Unknown
or duplicate keys, forbidden project settings, and invalid values are errors
after bounded configuration reads and before discovery, networking, or mutation.
Unrecognized SR_* environment names are errors too. Project context settings
may reduce message/character budgets, select minimal, or remove tool content;
they cannot widen a trusted user's disclosure settings. Exclusions and skill
roots merge as unions. Project roots must remain relative to the workspace;
only trusted user configuration can authorize absolute roots. Readers also check
symlink containment when opening files.
Credentials are environment-only and kept outside serializable configuration.
The v1 endpoint override is environment-only; model overrides use trusted user
configuration or SR_MODEL. Proxy, redaction-disable, and raw-retention settings
are reserved and rejected. The configuration contract
lists every key, bound, source layer, and restriction.
The endpoint accepts an empty or root path and rejects URL credentials, query strings, fragments, and non-root paths. Credentials, cache identity, and request allowance scope use the canonical origin. A development-only loopback HTTP exception cannot carry production credentials.
sr doctor --config shows non-secret effective values and their winning sources.
Disallowed overrides are configuration errors; no valid policy or fingerprint is
reported for them. Credential presence is reported without its value, and endpoint
overrides expose only their presence. --offline and --allow-network are accepted
here but conflict; this inspection command never sends a request.
The library's cli::ConfigFiles shares bounded initial configuration reads with
file refreshes. Refresh keeps the invocation's validated environment and CLI
layers, returning the current configuration and a boundary-specific comparison
against its typed policy receipt. Malformed, unreadable, or late reads fail rather
than authorize output. Rank orchestration and its HTTP/publication call sites are
not implemented yet; these library checks do not establish live ranking support.
Shared request allowances and snoozes are explicit trusted-user controls. Project
configuration cannot enable, raise, or disable the allowance.
--offline and --dry-run conflict with --allow-network. Case capture conflicts
with --dry-run and --no-persist. --no-cache and --no-ledger disable their
respective stores independently; --no-persist also disables persistent runtime
coordination. Ordinary configuration reads remain allowed.
Each provider attempt rechecks the effective disclosure and admission policy. Publication separately rechecks the fields governing eligibility and hook mode. A revoked permission or changed governing value withholds the affected action; it never authorizes a replacement request. An edit hidden by an unchanged CLI or environment override does not change the effective policy.
How Ranking Works
1. Establish the exact context
The Claude hook uses the incoming prompt as the current request, even when the transcript has not yet recorded it. Context, cursors, and feedback belong to a specific workspace, session, agent branch, and source adapter/producer. An explicit source that fails does not silently fall through to another conversation.
Native JSONL reads process complete records within a bounded tail. Replacement, truncation, compaction, and incomplete final lines are handled explicitly. An empty first transcript can still yield prompt-only context; malformed existing history is a different condition.
Windowing drops reasoning blocks, binary/media payloads, and prior sr advice.
Tool summaries preserve invocation/result association and useful failure lines.
The latest request gets budget priority, with explicit head/tail truncation for
oversized input. Redaction runs on complete bounded fields before truncation,
then on the assembled provider payload.
Explicit directives are resolved from the full bounded local request before redaction or truncation. The normalized input envelope carries local identity and events; it is validated and reduced to a separate provider schema, never sent wholesale to Jev.
A normalized import cannot update a native session's observations merely by
repeating its session ID. An input without durable session identity gets an
invocation-local namespace; unknown attribution disables durable session updates.
A terse “continue” needs a recoverable task anchor; an essential missing
instruction or attachment yields unavailable and a quiet hook, rather than a
guess from incomplete context.
Project signals use language/framework filenames, allowlisted tools on a trusted PATH, and bounded repository-relative dirty paths. Absolute workspace paths and branch names remain local by default. Git inspection disables filesystem-monitor hooks, optional index locks, and submodule traversal; an unsupported safe invocation omits the optional signal instead of executing project helpers.
The library boundary context::signals::collect implements these optional local
signals with an explicit authorized workspace and trusted executable roots. Its
250 ms stage intersects the invocation budget; status output is capped at 64 KiB
and 100 paths. Non-UTF-8/unsafe paths and partial inventories are reported rather
than treated as complete absence. Results are not serializable provider payloads:
callers must still redact them. sr rank collects them for every evaluation,
using fixed system directories (/usr/local/bin, /usr/bin, /bin) as its
trusted executable roots; the minimal context profile omits dirty paths.
Synchronous filesystem/spawn calls retain the subprocess boundary's documented
uninterruptible-kernel-I/O limitation.
2. Resolve what is loadable
An explicit --roster FILE replaces discovery. Otherwise, a harness inventory
or its visibility adapter determines roots, overrides, plugins, and load targets.
The presence of a directory does not mean the selected harness loads its skills.
Generic file mode uses explicitly configured roots and exposes uncertain visibility.
Each skill has an opaque stable ID, its actual invocation name, display name, source, content hash, load target, metadata, and visibility. Same-name skills remain distinct where the harness permits; shadowed or ambiguously invocable entries are excluded from hook suggestions.
For Claude, sr rank resolves same-name skills by Claude's documented
precedence (project skills over personal ones). That precedence is not yet
backed by conformance evidence, so every rank result carries
"visibility": "unverified", and the decision's first warning is
unverified-visibility: confirm that a suggested skill loads before relying on
it. sr roster and sr doctor claim no precedence at all. Names whose
authority is withheld (for example, a malformed file that could claim the same
name), ambiguous names and shadowed entries are never suggested.
Invocation restrictions are part of eligibility. Claude skills with
disable-model-invocation: true or an effective user-only restriction are excluded
from automatic advice; user-invocable: false alone does not exclude agent use.
A user-requested manual-only skill resolves as a manual_only reference, without
instructing the agent to bypass that restriction by reading its file.
| Resource | Default bound |
|---|---|
| Hook stdin | 1 MiB |
| Normalized context | 1 MiB / nesting depth 64 |
| Each user/project/policy configuration file | 256 KiB / nesting depth 32 |
| Explicit roster | 32 MiB / 10,000 records / nesting depth 64 |
| Transcript tail | 2 MiB / 2,000 records |
| Observation ingestion | 8 MiB per invocation; a separate committed cursor |
| One transcript record | 256 KiB |
| cass stdout | 8 MiB |
| Skill file / frontmatter | 256 KiB / 16 KiB |
| Discovery | 10,000 files / 32 MiB parsed bytes |
| Quill query | 128 distinct terms / 4,096 Unicode scalar values after escaping |
| Replay case capture/import | 16 MiB / nesting depth 64, with per-field limits |
| Local replay policy | 64 KiB / nesting depth 32 |
| Streamed evaluation dataset/report | 256 MiB / 10,000 case records / nesting depth 64 |
| Live decision or artifact summary envelope | 2 MiB / nesting depth 64 |
| Wide description | 160 characters |
| Rerank description / body excerpt | 1,000 / 700 characters |
| Serialized provider request / decoded response | 96 KiB / 2 MiB |
Reject duplicate keys and duplicate record definitions within each schema's collection/namespace in normalized inputs, rosters, configuration, replay/evaluation artifacts, frontmatter, and provider responses. The same skill can still be referenced across wide and rerank stages. Ambiguous skill metadata excludes that record and marks coverage partial. Configuration cannot execute interpolation or recursive includes. Evaluation imports use bounded streaming and enforce per-case limits as well as the total cap.
Source snapshots supply both hashes and excerpts. Before publishing a live
advisory decision or no-match claim, sr checks membership, precedence, and
indexed/wide content that
conditioned the decision, plus the entire shortlist's content and restrictions.
A changed candidate outside the shortlist or a new overflow match can invalidate
the result too. Explicit resolution checks every target and its name's precedence.
Trusted adapter generations can avoid a rescan only when they cover all required
dependencies. Otherwise bounded re-enumeration/content checks use the same deadline;
missing required validation withholds output. A runner-up cannot replace an answer
conditioned on stale alternatives. These are last-validation observations, not a
freeze of the filesystem; the harness still checks its later load. A supplied
roster replaces discovery but grants no new path access or invocation permissions.
sr roster and the roster check in sr doctor also inspect directories from
roster.roots in trusted-user and project configuration. Each directory uses the
<skill-name>/SKILL.md layout. Project roots stay inside the workspace, including
when symlinks are involved; trusted-user configuration can name absolute roots.
Configured roots extend the default Claude directories, and aliases of an
already opened directory are deduplicated. Reading a custom root does not prove
that the harness can invoke its skills: those entries remain unverified.
sr roster --snapshot FILE explicitly exports an owner-only manifest, bounded
to 32 MiB and 10,000 records, without implicitly overwriting an existing file.
Manifests can contain private skill names. sr roster --diff FILE compares the
snapshot with fresh authorized discovery in the same workspace, adapter, and
source namespace. It reports additions/removals, content and restriction changes,
shadowing, and invocation-name changes. Incompatible manifests are identified;
incomplete source coverage remains unknown rather than becoming a confirmed
deletion. Saved paths grant no new read access and cannot restore a removed skill.
3. Retrieve, then compare
Explicit requirements are resolved from the complete visible roster first.
They bypass probabilistic retrieval and cannot be vetoed by a low gate.
Every requested reference must resolve: missing, ambiguous, forbidden, or
conflicting references produce unavailable / explicit-resolution with separate
resolution records and no advisory API call. Successful explicit lists are not
truncated to top-K; the input limit is 32 explicit references.
For advisory ranking, Quill,
the native lexical engine in FrankenSearch, supplies BM25 retrieval when the
eligible roster exceeds 254 real skills. Every Choice also
includes __none__, for at most 255 total options. Retrieval uses the latest
request plus bounded task and error context; a terse “continue” retains useful
prior evidence.
| Eligible roster | Quill matches | Real skills admitted to Jev |
|---|---|---|
| 1–254 skills | Prefilter skipped | The full eligible roster |
| More than 254 skills | At least 254 | The first 254 matches under the deterministic ordering |
| More than 254 skills | 1–253 | Only those matches; the set is not padded with nonmatching skills |
| More than 254 skills | None | No provider call; unavailable / retrieval-empty, exit 5 |
Configured sizes first satisfy 1 ≤ K ≤ M ≤ 32. The effective rerank size is
min(M, admitted_wide_count), and the output cap is min(K, effective_M).
For example, three Quill matches produce a wide Choice with three skills plus
none, a rerank of at most three skills, and at most three returned suggestions.
A single match is valid and still competes against none. An initially empty
roster is a roster failure; a valid roster reduced to zero by explicit exclusions
or proven available references yields a local abstention.
SkillRanker embeds Quill's in-memory index through frankensearch-quill, with
default features disabled, the required frankensearch-core document types,
bounded indexing/query work, and the caller's Asupersync context. Names and aliases
are searchable titles; descriptions and tags are searchable content, rather than
stored-only metadata. Documents enter in stable skill-ID order and are committed
before querying. Cutoff ties follow the pinned document-ID mapping; re-sorting
an already-truncated result cannot recover an omitted tied candidate.
Queries are a deduplicated OR of escaped literal terms. Only the adapter adds
the OR separators; conversation text cannot introduce Boolean operators,
wildcards, ranges, or field syntax. The limit of 128 distinct terms and 4,096
Unicode scalar values applies after escaping and separators; analysis input and
work are bounded too. No analyzed terms means retrieval-empty. Parser
diagnostics and truncation are reported. A query that cannot be preserved safely,
exhausted query fuel, or an index failure yields unavailable output with quiet
hook fallback. Retrieval failures use exit 5; exhaustion of the overall
invocation deadline uses 6. Partial work never becomes a complete candidate set,
and there is no silent fallback to another engine.
Overflow results identify retrieval: "quill-bm25", the admitted count, and
engine/schema provenance. Building, committing, and querying the index consume
the same ranking deadline. A TUI can retain a matching roster index across
refreshes; a new hook process cannot assume an earlier process's index survives.
Quill is the only lexical search engine used by this project. Tantivy is not
used for runtime search, fallbacks, tests, benchmarks, or reference code.
The hybrid search facade, legacy lexical engine, Quill gauntlet, and optional
oracle/compatibility features are excluded. Dependency checks cover normal,
build, and development feature graphs. Quill verification uses native tests and
independent expected-result fixtures. SkillRanker does not need the fsfs
command, an embedding model, a search service, or an imported foreign index.
The wide pass combines a Choice, phase distribution, and three oriented gates:
needs_skill = mean(
specialized_method,
material_help,
1 - context_suffices
)
needs_skill is a heuristic score. Below the default 0.30 threshold, sr
abstains without a rerank. The wording includes planning, analysis, writing, and
explanation skills; acting on files is not a prerequisite for needing a method.
When the gate passes, up to eight real candidates proceed to a detailed Choice with another none option and one fit Noul per candidate. If none wins the wide comparison, the detailed comparison still runs when the need gate passes: richer skill excerpts can resolve ambiguity left by short descriptions. The client uses the TypeSafe HTTP API, preserving typed answers and validating every requested option before scoring.
4. Apply eligibility and rank survivors
A candidate is removed if it is excluded, below the fit threshold, or a reusable reference whose relevant content is proven present in the current context epoch. Workflows and unknown usage kinds remain eligible for repeat invocation. A changed shortlist invalidates the in-flight result.
The reusable-reference check needs evidence of the version and rendered content actually present. A source file's current hash cannot establish what a past read consumed. Changed arguments, dynamic content, forked execution, or compaction can invalidate reuse evidence; content presence never renews turn-scoped permissions.
Every remaining candidate must individually beat the none option's raw rerank
probability. Ties are excluded. If no candidates survive, sr abstains; local
priors and fit blending cannot re-admit a candidate that failed this check.
For each eligible candidate:
eps = 1e-6
clip(x) = min(1 - eps, max(eps, x))
log_odds(x) = ln(clip(x) / (1 - clip(x)))
utility_i = ln(clip(p_rerank_i))
+ w_fit * log_odds(fits_i)
+ w_prior * prior_delta_i
+ w_phase * phase_match_i
rank_score_i = softmax(utility)_i
Defaults are w_fit = 1.0, w_prior = 0.0, and w_phase = 0.0.
Priors and phase weighting are optional evaluated policy choices. A skill is
not penalized just because a previous suggestion went unobserved. After
compaction, uncertain loaded-state evidence cannot suppress a skill indefinitely.
Explain And Replay A Result
Find where a candidate was lost
--why-not SKILL_ID --explain follows a candidate through discovery, visibility
and restrictions, local policy, Quill admission, the wide shortlist, fit/none
eligibility, final ordering, and publication. It reports the first decisive
exclusion and any later stages actually evaluated.
sr rank --context scratch/context.json --allow-network \
--why-not SKILL_ID --explain --json
An unevaluated stage reports not-evaluated; an unknown ID reports
not-in-snapshot. Neither receives a fabricated zero fit. Explanations include
threshold operands, tie handling, content/policy versions, and bounded recovery
hints. They do not expand discovery, insert the target into a shortlist, change
the ranked result or provider request bytes, or add a provider call. Hints identify
actions and arguments for review; they do not execute commands or relax policy.
Save a case and compare local policies offline
Explicit capture turns a surprising result into a reproducible case:
# Opt in to retaining this run's bounded redacted inputs and recorded answers.
# Use a new output path in an existing private directory.
sr rank --context scratch/context.json --allow-network \
--save-case scratch/ranking-case.json --explain --json
# Reproduce the decision without accessing the source session or Jev.
sr replay scratch/ranking-case.json
# Compare compatible local thresholds/weights against the same recorded answers.
sr replay scratch/ranking-case.json --policy scratch/baseline.toml \
--compare-policy scratch/candidate.toml
A case binds the actual redacted request inputs, exact option maps and content
digests, validated recorded responses, eligibility evidence, policy, and
model/adapter provenance. It freezes the evaluation time, snoozes, loaded-state
and visibility evidence, ordered candidates, numeric priors/phase inputs, and
computation versions. Replay never consults today's clock, configuration, or
ledger priors. Synthetic fixtures and recorded provider answers are
labeled separately. Replay returns kind: replay with actionable: false,
keeping historical and recomputed decisions separate from live recommendations.
It makes no network requests, discovers no transcripts, executes no skills, and
writes no state. Only the explicit case and optional local policy files are read;
ambient user/project policy and credentials are not consulted. Embedded source
paths remain inert.
Local policy comparison requires the compatible inputs and responses needed by
that comparison. A completeness manifest identifies each captured stage. A
low-gate case can replay its original abstention without a rerank, but lowering
the gate needs the missing rerank. Missing required stages or an uncaptured prior
report not-replayable; a changed model, prompt, retrieval strategy, excerpt,
or shortlist needs new consented evaluation. Policy files accept only the
supported local ranking schema, without executable code or credential/routing
settings. A changed score is not evidence of better task outcomes. Ordinary
metadata-only history cannot reconstruct a case.
Exact parity covers decisions and numeric outputs under the same tested computation profile, not new invocation IDs, timings, or usage. Different numeric backends/build profiles need declared tolerances or report incompatible exact replay. An unavailable historical case can reproduce sanitized failure metadata without inventing an unobserved response. Additional privacy transformations remove any affected exact-input replay claim. Artifact digests need no original cache secret and cannot authorize importing a result into the live cache.
Capture is opt-in because redacted prose can remain confidential. All retained prose is redacted; credentials, hash keys, secret-bearing configuration, and response error bodies are excluded. Files are owner-only, created exclusively without overwriting existing targets, bounded to 16 MiB and nesting depth 64, and published only after a complete write. Publication uses a tested atomic no-clobber operation, including protection against a target or symlink raced into place, with a documented flush policy. A crash may leave a complete export whose delivery is unknown; filesystem publication and stdout are not atomic. Private, bounded partial files remain identifiable for explicit cleanup. These publication rules also apply to roster snapshots. Per-field limits still apply. An oversized case fails explicitly instead of dropping data needed for replay.
Capture consumes the invocation deadline; a failed requested write reports a
storage or timeout failure, preserving any already-incurred attempts and usage.
--save-case conflicts with --dry-run, --no-persist,
and hook mode. Imports validate bounds and internal consistency; a matching digest
does not establish trusted authorship. Imported labels and responses remain
untrusted evaluation data.
Local Feedback And Calibration
SkillRanker keeps observations and judgments separate.
| Record | What it establishes |
|---|---|
| Generated ranking | The selector produced a result |
| Successful advisory stdout write | Advice was emitted; harness consumption is still separate |
| Shadow evaluation | A prediction was recorded without exposing the agent to advice |
| Load attempt | A structured tool tried to load a resolved skill |
| Observed successful load | A resolved skill was loaded; its version can remain unknown |
| Not observed / unobservable / censored | The available record cannot establish an outcome |
| Explicit usefulness judgment | An assessor labeled a particular event and skill version |
sr stats puts usefulness, interruptions, and cost in one local report:
| Measure | Interpretation |
|---|---|
| Evaluated turns, emitted suggestions, valid abstentions, muted/suppressed output | How often the selector evaluates and interrupts |
| Operational failures, latency, cache reuse | Availability and overhead across the disclosed cohort |
| Observed loads, observation coverage, censoring | What was seen, with missing evidence kept visible |
| Independently judged useful suggestions and label coverage | Usefulness for the judged cohort |
| HTTP attempts, known tokens, unknown usage, estimated cost | Recorded consumption and the limits of its accounting |
Cost per judged-useful suggestion uses only that cohort's matched attempts and labels. With no useful labels, the ratio is not estimable. Unknown usage or missing/inapplicable pricing also prevents an exact monetary ratio; report known attempt and token counts instead. Unlabeled traffic does not inherit measured usefulness, adoption is not task success, and token savings are not estimated labor savings. Reports contain no raw examples by default and do not enable adaptation or advisory mode.
Shadow, advisory-hook, CLI, and TUI records have separate denominators. Writing zero bytes in shadow mode does not count as delivered advice. A successful path-only read can establish a load but cannot establish the consumed version for suppression or version-specific feedback.
Observation ingestion has its own cursor, separate from the bounded ranking
window. Observations, loaded-state evidence, and cursor advancement commit
together; unread events remain a backlog, not skipped history. Attribution uses
the latest preceding emission with a known boundary in the same agent/turn.
Ambiguous concurrent delivery remains unknown, and identical prompt text does
not merge distinct turns. sr observe can reconcile the final turn without
waiting for another user prompt.
Observation requires an explicit source, with the same meaning as ranking:
--transcript FILE --harness NAME is native, --session PATH selects cass, and
--context FILE selects a normalized producer. Those namespaces remain distinct;
matching a session ID or path cannot authorize updating another adapter's cursor.
Unknown durable identity is an error. sr observe never calls Jev and requires
a ready ledger; --no-ledger and --no-persist conflict with its promised update.
sr observe --transcript scratch/session.jsonl --harness claude_code
sr stats --since 7d --by-skill
sr feedback EVENT_ID --skill SKILL_ID --verdict useful
sr eval --dataset scratch/labeled-cases.json
sr calibrate --evaluation scratch/evaluation-report.json
sr calibrate --evaluation scratch/evaluation-report.json --apply
When a different skill would have helped, record the correction directly:
sr feedback EVENT_ID --skill ORIGINAL_ID --instead BETTER_ID
The two distinct candidates resolve against historical membership, versions, and
advisory eligibility. The ledger retains bounded, deduplicated membership
snapshots of the full roster, including skills outside the shortlist, without
their bodies or descriptions. An alternative that was manual-only or excluded
was not a missed advisory candidate. The correction records the original as
unsuitable and the alternative as useful in one atomic transaction, with
assessor provenance and expected label revisions. A failed alternate lookup
cannot leave an unintended standalone negative label. --instead and
--verdict are mutually exclusive.
It is partial, unblinded feedback: other candidates remain unjudged, and the
correction cannot become an independent blinded holdout case.
A known-absent alternative becomes a separate prospective proposal. A missing
historical snapshot means unknown membership, not absence, and prevents an
unsupported historical correction. Changed or unknown versions need separately
identified evidence. Neither case establishes that the original selector missed
an available candidate.
For “not now,” use snooze instead of
a negative usefulness label.
Calibration uses independently judged positive, no-match, and near-miss cases, with separate training, validation, and final-test task families. Priors are fit on training data, thresholds are chosen on validation data, and the frozen policy is evaluated once on the final holdout. Future labels cannot enter an earlier case's prior snapshot. The default tuning loss is:
| Outcome | Loss |
|---|---|
| Correct suggestion or correct no-match abstention | 0 |
| Abstention on a positive case | 1 |
| Incorrect suggestion, including a needless suggestion on a no-match case | 2 |
| Operationally unavailable result on an actually attempted case | 2 |
Compare policies on the same predeclared judged cases and denominator. An
operational failure keeps its unavailable status and separate failure count;
its loss penalty prevents dropping hard cases from making a policy look better.
On equal loss, prefer fewer failures, then the frozen baseline. A policy that
always stays silent still incurs misses on positive cases.
Bounded-loss sampling uses this loss divided by two; reports identify the scale
and keep it distinct from a binary error rate.
Missing replay evidence is different from an observed failed attempt. A policy whose required responses are absent is not estimable; do not invent a result or remove those cases to complete its comparison. Lowering a gate needs rerank evidence, and changing retrieval, shortlist size, prompts, or model can require new evaluations. A batch stopped before finishing the declared cohort remains partial and cannot promote a policy.
Experimental priors use centered, shrunk Beta(1, 4) estimates from judged usefulness. They are disabled by default and can only reorder eligible candidates. A fixed observation count alone never enables learning.
Policy rollback follows the same preview/apply workflow:
sr calibrate --rollback POLICY_REVISION
sr calibrate --rollback POLICY_REVISION --apply
Rollback restores only managed ranking-policy fields after compatibility, digest, and conflict checks. It preserves current credentials, networking authorization, hook installation, feedback, and unrelated settings; it does not replace the entire configuration with an old file.
Description diagnostics flag missing metadata, duplicate visible prefixes, and rerank disagreements. Gap reports identify suspected missing coverage while showing retrieval and context quality. Text clustering requires separately enabled retention of redacted excerpts; metadata cannot reconstruct a private request. Quill retrieves retained examples and candidate neighbors; optional local clustering operates on those examples. Neither command edits skills or invokes a skill manager.
Evaluation, Sampling, And Risk Monitoring
SkillRanker evaluates whether a recommendation helps under the user's current constraints and the harness's permissions. Labels come from independent review of the full visible roster. Each case can have several acceptable additional invocations, or none. Explicit-name resolution has its own tests and is excluded from advisory quality metrics.
Measure the whole selection pipeline
| Metric | What counts |
|---|---|
| Candidate coverage at 254 or M | Fraction of positive advisory cases where the candidate set contains at least one acceptable skill |
| Top-one precision | Fraction of emitted advisory suggestions whose first skill is acceptable |
| Positive-case suggestion rate | Fraction of judged positive cases receiving an acceptable first suggestion; abstention and unavailable count as misses |
| Needless-suggestion rate | Fraction of judged no-match cases receiving any advisory suggestion |
| False abstention | Fraction of judged positive cases receiving a valid relevance abstention; operational failures are reported separately |
| Fit Brier score | Mean squared error of fit estimates on the declared set of independently judged skill/case pairs |
Candidate coverage asks whether the selector finds any acceptable option. Optional set recall measures the fraction of all acceptable skills recovered and is reported separately. This matters when a case has twenty acceptable skills but the shortlist holds eight.
Benchmark retrieval and rerank stages run independently of the production gate so low-gate cases do not disappear from the comparison. Reports also show the actual gated decisions, operational failures over all attempted cases, and counts of unjudged cases. Baselines include Quill-only lexical selection, the cookbook-style selector, Choice-only, fit-only, and the default blend, with both recent-context and latest-request-only inputs.
Reports bind results to dataset and split digests, roster content and visibility, prompts, policy, runtime, and returned model identities/time ranges. Related sessions and task variants stay in one split. An unversioned Jev alias limits reproducibility even when the local sample and arithmetic replay exactly.
Robustness evaluation includes option-order permutations, opaque-ID renaming, equivalent whitespace, duplicate-looking descriptions, irrelevant decoys, long distracting text, and hostile ranking instructions. Local transformations with unchanged semantics have exact parsing/scoring invariants. Jev comparisons report decision and coverage changes rather than demanding identical probabilities from a stochastic, candidate-dependent model. New provider inputs require new responses and consume the live budget. Related variants retain their original task family and split.
Spend the evaluation budget deliberately
--sample-size freezes a sampling manifest before selected cases run. Unless
replaying a recorded draw, omit --seed for a fresh seed from trusted OS randomness.
The unit is one representative per independent task family, selected by a
recorded rule before inspecting evaluated outcomes. The manifest records:
- The consented frame, split digests, and observable strata, such as normal versus overflow retrieval and complete versus degraded input.
- Stratum population and sample sizes, selected IDs, and justified inclusion probabilities for random selection without replacement within a stratum; diagnostic selections leave these probabilities unknown.
- The RNG algorithm, version, seed, randomization provenance, design status, policy/model identities, and label/request budgets needed to replay selection.
A manually fixed seed supplies reproducibility, not evidence for the claimed inclusion probabilities or design-based uncertainty. Such a run is diagnostic unless its input carries a matching prior randomization manifest; replaying that manifest is the same draw, not an additional independent sample. Imported provenance remains supplied/unverified. Full-frame evaluation needs no sampling randomness. Guarantees assume uniform selection within strata; the recorded PRNG and bounded-draw checks support the implementation without proving exact uniformity over every possible seed. Never redraw to obtain favorable results.
Every represented stratum receives a positive allocation. Gate-abstained, operationally failed, and unknown-metadata cases remain in the sampling frame. If the budget cannot cover it, the report must use a declared narrower population or a predeclared stratum-merging rule.
Oversampling rare cases helps inspection, but changes the sample's composition.
For error indicators or another declared loss in [0,1], the report weights each
stratum by its share of the original frame:
weight_h = population_h / total_population
estimated_loss = sum_h(weight_h * sampled_mean_loss_h)
For example, take a frame of 900 routine cases and 100 overflow cases, with 50 sampled from each. If the observed error rates are 2% and 20%, respectively:
| Calculation | Error estimate |
|---|---|
Unweighted sample: (0.02 + 0.20) / 2 |
11% |
Frame-weighted: 0.9 × 0.02 + 0.1 × 0.20 |
3.8% |
The design-weighted mean is a Horvitz–Thompson estimate for that declared frame. It does not establish risk on unseen projects. Weighted precision is a ratio estimate, with its own uncertainty requirements; weighted rows cannot be treated as ordinary binomial counts. Sampling estimator reference
Fixed-sample bounded-loss reports use a conservative per-stratum sampling bound with a shared error budget; a fully enumerated stratum contributes its exact mean. Missing labels receive lower/upper loss assignments rather than silently shrinking the denominator. Unknown inclusion probabilities or a changed frame invalidate the corresponding estimation claim. These reports supplement the separate promotion cohorts below.
Near-threshold decisions, wide/rerank disagreements, overflow misses, and sparse categories also enter a diagnostic review queue. That queue helps choose examples to inspect; it is kept out of representative holdout denominators. Optional allocation based on pilot variance and labeling cost is compared at equal label, token, and request budgets before adoption. Sampling never changes which skill the live agent is instructed to execute or adds work to the hook path.
Require evidence before promoting a policy
The acceptance policy fixes these requirements before tuning. They are evaluation thresholds, not claimed benchmark results:
| Check | Requirement |
|---|---|
| Relevance cohort | At least 300 adjudicated primary cases from independent task families: at least 150 positive, 100 no-match, and 50 near-miss cases across those groups |
| Overflow candidate coverage at 254 | At least 98%, with at least 50 positive overflow cases |
| Shortlist candidate coverage at M | At least 95% on positive advisory cases |
| Top-one precision | At least 90%, with the lower endpoint of a 95% interval at least 80% |
| Positive-case suggestion rate | At least 80%; abstention and unavailable remain misses |
| Needless suggestions | At most 5%, with the upper endpoint of a 95% interval at most 10% |
| New-harm risk | One-sided 95% upper bound at most 2%, from a separate controlled paired cohort |
| Operational fallback | At most 5% over at least 500 representative hook invocations, including provider outages |
Primary relevance rates use two-sided 95% Wilson intervals, with one preselected case per independent task family. Additional variants do not inflate that denominator. Deliberately oversampled benchmark categories do not establish production prevalence or production precision.
Independent families alone do not establish a binomial sampling model or remove selection bias. Freeze the population, sampling design, endpoint model, and interval method before inspecting results. Wilson intervals are nominal model-based intervals, not exact coverage guarantees for an arbitrary fixed or quota-selected benchmark. Stratified probability samples use design-aware bounds and separate ratio methods; weighted rows are not binomial counts. Diagnostic rates without a justified inferential model cannot pass promotion gates.
The harm comparison pairs advice-enabled and baseline runs from equivalent isolated snapshots, with identical permissions and budgets, randomized arm order, and blinded outcome review. A task family counts as new harm if any planned paired run is harmful with advice and non-harmful without it. Missing or unjudgeable outcomes are also flagged for this conservative harm-or-unresolved gate. Count each family once and report judged harm and unresolved-only outcomes separately. Improvements on other tasks do not cancel flagged units; the net harm difference is reported separately.
The gate uses a one-sided 95% Clopper–Pearson upper bound when the prespecified
binomial model is justified; otherwise it needs a prespecified design-valid
bound. With zero flagged events in n independent identically distributed
task-family units, the binomial upper bound is
1 - 0.05^(1/n): about 2.95% for 100 units and 1.98% for 150 units.
Repeating one task 150 times does not provide 150 independent units, and zero
observed events does not justify a zero uncertainty interval.
Exact binomial interval reference
Insufficient independent cases, no emitted suggestions, or missing subgroup coverage means the requirement is not established. The harm cohort is separate from the relevance holdout, and its result applies to the declared population and experiment.
Monitor repeated evaluations without resetting the evidence
Optional sequential monitoring tracks a new-harm-or-unresolved composite from prospectively ordered, controlled task-family pairs. A finalized unit is flagged for judged new harm or an outcome still unjudgeable at its frozen deadline. Each family counts once; reports separate judged harm, unresolved-only, and unflagged units. The null bounds the conditional probability of this composite at 2%, using a fixed mixture of alternatives at 5%, 10%, and 20%. Under that conditional-risk assumption, its evidence threshold controls the chance of ever raising a false alarm across repeated looks. Time-uniform evidence reference
This is not a false-alarm guarantee for true harm alone under arbitrary missing labels. With zero actual harm but 5% unresolved units, the composite already exceeds the 2% null. An alarm can identify inadequate evidence without demonstrating harmful advice.
The mixture starts at one and alarms at 1 / alpha_monitor; a monitor allocated
alpha_monitor = 0.05 has threshold 20. Each finalized unit contributes once,
in the predeclared order. Pending labels wait until their adjudication deadline.
Labels can change before finalization; the analyzed label version is then frozen.
A later correction invalidates the inferential epoch: preserve its original
trace, alarms, and spent alpha. Corrected historical recomputation is descriptive
only. Resume inference with fresh prospective units and a fresh allocation unless
a separately proven revision-aware method is available.
The total false-alarm budget is allocated across monitors and restarts in advance.
A restart does not replenish it. State is bound to the rubric, baseline,
policy/model cohort, and ordering; missing or corrupt state or an unresolved
label revision reports unmonitored.
An alarm blocks further policy promotion and recommends the frozen baseline or
shadow mode. Configuration changes still require the explicit apply path.
The absence of an alarm does not establish that the risk ceiling is met, and an evidence value is not a posterior probability. The monitor cannot be applied to an arbitrary fixed sample merely because its sampling estimate is valid, or infer harm from skill adoption. It runs on evaluation evidence without launching production experiments or making extra network calls.
sr eval --explain includes mathematical explanation cards with equations,
substituted values, assumptions, and what further evidence would change the
conclusion. These belong in reports; hook advice stays short.
Opt-in retrieval and description experiments
The evaluation tools support three bounded experiments. They use held-out cases and the existing request, disclosure, and latency budgets; they do not silently replace the default selector.
| Experiment | Scope and comparison |
|---|---|
| Relevant skill passages | Compare the fixed opening excerpt with Quill-selected heading-delimited passages inside an already selected skill, retaining its purpose/restriction prefix and the 700-character body budget |
| Multiple overflow query views | Compare the combined query with up to three Quill views: current request, task anchor, and recent error; deduplicate and fuse their rankings into at most 254 candidates |
| Description overlays | Compare an original description with a proposed evaluation-only overlay against user-supplied positive and near-miss cases |
Passage retrieval reads only already authorized, bounded skill bytes, scans them for secrets before excerpting, and retains heading, position, and version. A successful query with no passage match uses the declared opening excerpt; parser, fuel, and index errors retain typed failure behavior. It loads no referenced files, scripts, or additional candidates.
Multiple query views share aggregate term, character, fuel, memory, and deadline
budgets. Each returns at most 254 matches. A pinned reciprocal-rank rule such as
sum_v 1/(60 + rank_v) uses ranks starting at one, zero contribution for absent
hits, equal view weights, and stable skill-ID ties. Identical normalized views
are deduplicated. An attempted view failure invalidates the result; an empty
union remains retrieval-empty. Fusion scores are retrieval heuristics, not
Jev probabilities, and rosters of 254 or fewer still bypass the prefilter.
Description overlays bind to source content digests and are unavailable to hooks. A changed description requires a new request fingerprint and responses; compatible unchanged inputs may use recorded answers. Reports compare coverage, false suggestions, truncation, unknown cases, and attempts. Stale or ambiguous sources are rejected. The experiment exports a reviewable suggestion without rewriting skills or changing loadability. Untouched task families supply final validation; a better prose rubric score alone cannot justify promotion.
Control Requests And Interruptions
Share an HTTP-attempt allowance across sessions
An optional trusted-user allowance bounds HTTP attempts across local sr
processes in a configured user/endpoint scope and named time window. It includes
retries and live evaluation, and intersects each invocation and batch limit.
Setup explicitly initializes the allowance and preflight reports the maximum
attempts. It is neither a cross-machine billing limit nor a hard monetary cap;
cost estimates use separately versioned prices and retain unknown usage.
sr budget # Inspect local allowance and health
sr budget --max-attempts 100 --window 1h # Preview scope and window semantics
sr budget --max-attempts 100 --window 1h --apply # Explicitly configure this limit
The allowance accepts 1–10,000 admissions in fixed one-hour UTC windows. The preview
names the user/endpoint scope, exact window boundaries, charged attempts, and
remaining allowance. This is not a rolling-hour cap: adjacent windows can each
consume their allowance close to the boundary. Configuration changes preserve
still-applicable charges. Accounting shares the 64 MiB cache/coordinator budget,
but unexpired charges cannot be evicted; exhausted storage withholds new requests.
Under the trusted configuration lock, setup first publishes a durable activation
intent and new guard generation, then prepares accounting while preserving
charges, then publishes the matching ready generation. Missing or mismatched
state after the intent blocks admission. Retrying --apply resumes that intent;
before it is visible, the old policy remains in force. Each attempt checks the
active generation at admission, including processes started earlier. Already
admitted requests cannot be recalled. Inspection itself makes no network requests.
Persistent attempts hold the same bounded lock as setup while rereading guard
configuration, validating the accounting generation, and debiting. They release
it before HTTP; lock contention cannot turn into an unbounded hook wait. With
--no-persist, the final trusted-config read is the local admission boundary
only when no guard is active. An intent or enabled guard refuses the request;
a later activation cannot revoke a stateless request already admitted. Retries
must recheck, not reuse startup-time settings.
Each attempt is atomically reserved/debited before transmission. Its single-use permit binds an attempt ID, request/endpoint, guard generation, window, and remaining monotonic dead
Comments