Anatomy of okl · Keeps your coding agent true to your rules, specs and docs. Keeps them true to the code. Keeps learning as you build.

Load your rules.
Hold your agent to them.
Keep learning as you build.

Start okl with the rules, ADRs, standards and specs you already have, loaded as lessons that cite their source. It briefs your coding agent with the ones that share each task's words, on every prompt in Claude Code and on request in other MCP agents, and when the code a lesson covers changes, the pre-push hook, where it is installed, holds the push until its check passes again, or the code is back to what passed. New rules, mistakes, drift and failing tests keep adding lessons as you build.

6

Re-checked or retired

You decide

okl reverify lists each stale lesson's stored check and re-runs it once you confirm. A pass stamps the lesson again; a fail means fix the code, or change the lesson on purpose.

Claude Code When the agent first stops after changing files, it asks what the work taught, listing up to five lessons it was briefed on and five whose files changed. okl update keeps the proof unless the files change; okl retire keeps the reason.

5

Held when code moves

okl runs it

Commit a change to a file a lesson covers and its proof is stale. The push waits, and any briefing that includes the lesson marks it STALE, until its check passes again or the code is back to what passed.

It compares content with the proven commit, so putting the code back clears it. okl init installs the hook in that clone, and --no-verify skips it; okl init --ci gates every PR in CI instead, and --git-hook keeps both.

1

Where lessons come from

Up frontyour rules, ADRs, standards and specs, plus starter lessons for your stack As you buildnew rules, mistakes, drift the gate finds, failing tests, reviews

Claude Code /seed-from-docs proposes lessons from your docs into a file you review before okl seed imports it.

This one: a review found get_order hands any customer's order to any signed-in user.

you decideokl runs it
4

Briefed before each task

okl runs it, in Claude Code

okl shows up to five lessons that share enough of the task's subject, lists the rest that matched in a line each, and puts them in the agent's context first. A lesson no check backs is marked UNCHECKED, as advice rather than proof.

Claude Code A hook runs on every prompt; if okl can't brief, the prompt is blocked or told so. Other MCP agents can call okl_check; nothing calls it for them.

2

Written as a lesson

You decide

You or your agent write it down in one shape: when you see this, do that, because. It can name the files it covers.

Scope repo briefs only here; scope org can reach other repos once it is in a pack, a JSON file in git they load with okl seed. The store is one gitignored file in each clone; no server needed.

3

Proven by your check

okl runs it

okl runs the check you chose and stamps the lesson only when it passes, keeping the command, the commit and the time.

A pass is exit 0, plus the text you expect if you name one. For a lesson that covers files, a stamp with no observed run counts as unproven. Every run is logged, pass or fail.

One lesson, all the way round

Real okl output from a throwaway repo (2026-11-02), shortened where … marks a cut. The # notes are ours.

orders-api
2
$ okl record --type Rule --scope repo --id order_scope --files app/orders.py --tags security \
    --title "Order lookups are scoped to the signed-in customer" \
    --symptom "an order is fetched by id with no check that it belongs to the caller" \
    --fix "filter the lookup by the caller's id; another customer's order is a 404, not a 403" \
    --trigger "a customer could read someone else's order by changing the id in the URL" \
    --body "Without the owner check, any signed-in user can read any order by trying ids (IDOR, CWE-639)."
order_scope
3
$ okl verify order_scope --run "pytest -q" --expect passed
✓ verified order_scope — Order lookups are scoped to the signed-in customer
  evidence: `pytest -q` exit 0, matched 'passed' on commit e19f9e203884 @ 2026-11-02T01:40Z
…
4
$ okl check --task "Add an endpoint that returns one order by id for the signed-in customer"
## OKL briefing — orders-api · task: Add an endpoint that returns one order by id for the signed-in customer
_4 relevant node(s) from the org's encoded body, and 4 more listed by title._
…
# third of four, all on point: an IDOR rule and a tampering rule from a .NET pack, this one, and the test it needs:
- **FIX: Order lookups are scoped to the signed-in customer** [order_scope] — when you see: an order is fetched by id …
    → filter the lookup by the caller's id; another customer's order is a 404, not a 403
    why: Without the owner check, any signed-in user can read any order by trying ids (IDOR, CWE-639).
# proven above, so unmarked; the fourth, from a seed pack and never checked here, is marked:
- **FIX: Authorization behavior is only proven by …** [seed:dotnet-canon:r_authz_test_required] *(UNCHECKED)* …
…
> 7 lesson(s) marked UNCHECKED have no observed check behind them: treat them as advice, …
# in Claude Code, a prompt hook puts this briefing in the agent's context before every turn, and shows the person:
okl · briefed 4 lesson(s): Missing ownership scope check is an IDOR…; Server-controlled fields trusted from the…;
  Order lookups are scoped to the signed-in… (+1 more) · 4 related listed for the agent
5
# later, someone "simplifies" app/orders.py:
-    return order if order and order["owner"] == user else None
+    return order
$ git commit -qam "simplify get_order" && git push origin main
OKL drift: 1 rule(s) may be stale (source changed after verification):
  • [order_scope] Order lookups are scoped to the signed-in customer
      files: app/orders.py
…
okl pre-push: lessons drifted (above). Re-check them with 'okl reverify', commit,
  then push again. 'git push --no-verify' skips this check.
error: failed to push some refs to '…/remote.git'
6
$ okl reverify --yes
okl reverify will run 1 stored check(s), from the store:
  [order_scope] pytest -q
✗ check FAILED (exit 1) — NOT stamping verification.
…
FAILED tests/test_orders.py::test_another_customers_order_is_not_found - Asse...
…
okl reverify: 0 re-verified, 1 failed, 0 need a first check.
# the change had dropped the owner check; put back, the file matches its proven content again:
$ git commit -qam "restore the owner check" && git push origin main
OKL drift: OK — 1 rule(s) checked, no encoded rule's governed source changed after its last verification.
…

The encoding loop

Where a lesson lives

Whether a rule was decided up front or taught by a mistake, a drift or a review, put it where the next person or agent will meet it. If it keeps being broken, move it somewhere stricter, up to a failing test.

Briefing onlyUseful for some tasks. Most lessons.
okl
Watched for driftCovers specific code: files and a check.
okl
Build-breakingBroken before, or costly: a failing test.
your CI

A line in CLAUDE.md or AGENTS.md suits the rare lesson every session needs. Moving a lesson is a person's call.

Measured

What the briefing did

42%→7%
68 of 163 unbriefed11 of 164 briefed

attempts that repeated a known defect

Seven A/B runs on eight tasks built to invite a defect, three samples per arm: sonnet writes one-shot code with the briefing put in its prompt, and haiku judges it blind. The runs (Aug 30 to Sep 3) predate today's subject-word search. Not measured in this A/B: the hook, the write side, improvement over time. The gate's drift was measured on its own (REPORT §4ae, §4af, §4ah). The example above shows how the parts work, not that they help.

Receipts in evals/results/: ab-20260830-0003, ab-20260901-0133, ab-20260901-1238, ab-20260902-0538, ab-20260903-0157, ab-20260903-1214, ab-20260903-1323 · evals/REPORT.md §5, finding 7

Under the hood

How it ranks, what it costs

Rank
the prompt's subject words, up to 40 → BM25 in SQLite full-text search (title 8, body, symptom, aliases and triggers 4, fix 2, tags 1) → 36 fetched → filtered by scope, and org lessons by applies_to and the repo's interests → shown in full if three subject words, or two rare ones, are in its title, symptom, aliases or triggers, at most 5 → the rest that matched named in a line each, up to 12
Cost
about 1,580 tokens for a full briefing, a median of about 720 on real prompts before the briefing's marks (about 5% more), and about 0.1 s for okl check; no model runs until you turn one on. Of the three optional local models, the warm one runs once okl warm index has embedded the store, adding what a lesson means at a median of 114 ms a prompt and about 1.6 GB of memory
Stale
the committed content of the lesson's files differs from the proven commit (commit times, if that commit is missing); a lesson with files and no observed pass holds the push too

Where it breaks

  • Without the warm model, a task that shares no exact word with a lesson misses it: no stemming, so "orders" won't match "order".
  • A lesson named in a line is not shown: an agent told to output only code opened one in at most 1 of 24 runs (REPORT §4ab); a working session is not yet measured.
  • A file is watched whole: any edit needs a re-run.
  • A check proves what it tests, and nothing more.
  • Drift prompts a re-check; it is not yet shown to catch most breaks (REPORT §4af, §4ah).