Skip to content

0.8.3

Choose a tag to compare

@emeraldleaf emeraldleaf released this 11 Oct 01:51
890b269
  • Retiring a lesson is a verb, with a reason (okl review R2). "Retracted" was the only
    way out, retracting meant re-recording (which wiped the lesson), and okl check never
    read SUPERSEDES, so a replaced lesson was briefed beside its replacement.

    • okl retire <id> --reason "..." (and the okl_retire MCP tool) keeps every field.
      Without a flag the lesson is wrong: status retracted, briefed as AVOID. --by <new-id>
      marks it superseded and writes <new> SUPERSEDES <id>: briefings leave it out before
      the cut, so it never takes a live lesson's slot, and print one line pointing to the
      replacement by id. --obsolete takes it out of briefings. A resolved defect is not
      retired and keeps briefing against its return.
    • The reason goes to a new retirement table rather than a column on the lesson, so an
      older okl rewriting the lesson's row cannot lose it. okl show and okl_get list a
      lesson's retirements.
    • Only okl retire changes a retirement: okl update and okl record refuse the retired
      statuses, and neither okl seed (eight bundled lessons say "live") nor --replace
      brings a retired lesson back.
    • A replacement must be live and visible wherever the lesson it replaces was, which also
      rules out a cycle. The pointer follows the latest replacement through a chain to a
      live one; a superseded lesson with no replacement this repo can see stays briefed.
      Retired lessons no longer use up the search's window. Retiring a lesson again with
      the same reason and replacement changes nothing once the first call has finished; a
      retry after a call that failed part-way finishes it without logging the reason twice.
    • Drift, the committed snapshot, the governs-nothing advisory and the briefing's STALE
      mark skip retired lessons, and okl retire on one that governs files refreshes
      okl-drift.json (the MCP tool cannot, so it says to run okl export --drift).
      Exposure metrics skip them too, and okl dedup recommends okl retire --by instead
      of a hand-written SUPERSEDES link okl check never read.
    • A shared service gets POST /retire.
    • Upgrade every okl that writes to a store before retiring lessons in it: an older
      okl's seed resets a retired status, and its verify refuses the new statuses.
  • Refining a lesson no longer wipes its proof (okl review R1). Recording a lesson again
    with its --id, which the Stop hook, /record and the encoding-loop skill all taught as
    the way to refine one, replaced the whole row: its verification, its governed files and
    its creation date were lost, and it quietly left drift.

    • okl update <id> --<field> ... (and the okl_update MCP tool) changes only the fields
      given and keeps the rest, the proof and the first creation date included. Changing
      --files clears the stamp, because a check of other files proves nothing about these;
      it keeps the stored check, so okl reverify re-runs it, and refreshes okl-drift.json
      as okl verify does. An empty value clears an optional field, --ttl-days "" too.
      Updating a seed: lesson warns that the next okl seed restores the pack's text.
    • okl record --id <existing> is refused (exit 2), naming okl update; --replace
      (which needs --id) overwrites the lesson deliberately and keeps only its first
      creation date. The MCP okl_record tool does not offer replace: an agent is pointed
      at okl_update. okl seed still updates its own lessons in place, and now keeps their
      first creation date too.
    • okl show <id> (and okl_get) prints one lesson whole, with its proof.
    • Every briefed lesson carries its [id], so an agent can act on a lesson it was just
      shown. That adds about 150 tokens to a full briefing on the bundled packs (~1,620 to
      ~1,770; receipts in evals/results/), and REPORT §4j registers it as a change of
      treatment for the A/B. Its run (2026-10-10, ab-20261010-2314.json) read 4/23 unbriefed against 0/23
      briefed (one briefed sample returned a plan, not code, and is not counted; #164): the ids are safe on the task set, and the unbriefed arm's own 26-point fall
      from §4i starts a new series. The layout pre-flight confirms no content was lost, and its
      summary line no longer prints a growth as "-5% smaller".
    • A shared service gets POST /update (a closed, typed body), GET /node/{id} (any id,
      / included; an unknown one is {"node": null}, so a 404 means an older service) and
      replace on /record. Its read-modify-write endpoints are serialised, since two
      concurrent updates lost a change without an error. A client recording an id against a
      service older than this refuses, instead of letting it overwrite: upgrade the service
      with the client. Every write now checks that text fields are text and ttl_days a
      whole number, so a wrong type is refused before it is stored. The client also closes
      every HTTP error response it catches: each holds its socket, and R1 made 400s and 404s
      routine, so the leak failed the Python 3.14 suite.
    • The Stop hook, /record, the skill, the README and the guide now teach okl update.
  • The MCP server reports okl's version (#159). Its initialize reply carried an empty
    serverInfo.version on mcp 2.x and the SDK's own version on mcp 1.x, because okl passed
    none. It now passes the installed okl version on both, and a test reads it from a real
    initialize over stdio. The MCP build test also reads mcp 1.x's tool replies now, so it
    passes on both SDK majors as its docstring claims.

  • The Stop hook, the encoding-loop skill, CLAUDE.md and the README no longer say an org
    lesson reaches every repo.
    By default each repo has its own store, so a lesson recorded
    with --scope org reaches another repo through a pack that repo loads. They now say
    that; org still means a lesson true in any repo.