Skip to content

Releases: HarperZ9/learn

learn 2.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 23:26
e153460
  • Choice items (learn-choice/1, src/tutor/choice.mjs). A recall question keys one answer and names the misconception behind each wrong choice. diagnoseChoice() tells a wrong attempt which misconception it matches, using the same top levels as the arithmetic tree, and never returns the keyed answer. doctor checks that with a probe and a known-bad item.
  • A browser entry, @harperz9/learn/browser (src/browser.mjs). Practice, scheduling and diagnosis load in a page with no bundler. The session functions moved from tutor.mjs into session.mjs, which has no Node built-ins; tutor.mjs re-exports them unchanged. A test walks the entry's import graph and fails on any node: or bare import.
  • An attempt may carry a misconception leaf, and misconceptions() counts it.

learn 2.2.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 21:12
4edb19c
  • Misconception diagnosis for arithmetic practice. A wrong answer to a whole-number or fraction-addition prompt walks a fixed tree of yes-or-no questions and gets a cause, such as misrecruited.no_carry or slip.one_digit. tutor misconceptions and learn_tutor_misconceptions now count causes per objective in a diagnoses field. The diagnosis never shows the correct answer. On 50 held-out teacher-labelled answers it agreed with the teacher on 96%; details in docs/MISCONCEPTION-DIAGNOSIS.md.

learn 2.1.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 07:43
33b317c
  • Add Windows x64 MCPB and ZIP packages with a bundled runtime, scoped skills and qualified local stdio workflows.
  • Require clean exact-tag source, versions ending in .0, payload hashes and matching same-release artifacts; refuse changed release reruns.
  • Preserve the graded-assessment halt and CLI-only live course actuation; validate assessment outcomes rather than field-name substrings.

All notable changes to learn. Versions follow semantic versioning; each minor release was built
behind the feat/learning-loop branch and reviewed before merge.

learn 2.0.0

Choose a tag to compare

@github-actions github-actions released this 27 Sep 19:05
8d99e98

A security release that also makes the package's own entry points work. It changes where learn
keeps state and how resume authorizes steps; "Moving from 1.6.0" below lists what to do.

Security

Affected: 1.6.0 and earlier.

  • Path escape through sessionId and runId. learn_tutor_plan joined sessionId into
    tutor/<id>.json with no check, so "../.claude/settings" replaced a project's
    .claude/settings.json. learn_tutor_record, learn_verify and learn_receipt read or
    rewrote files the same way, and learn_dry_run, learn_tutor_prooflesson and
    learn_tutor_reverify read any path, with a non-JSON file's first characters in the error.
    Ids now match [A-Za-z0-9._-]{1,64}, must not start with a dot or a hyphen and must not be a
    Windows device name. Every resolved path, after links and junctions, must stay inside the state
    folder. Path arguments resolve inside it, and a failure carries a closed code and fixed text. A
    path outside the folder on its text, such as a \\host\share UNC path, is refused before
    anything opens it, so no SMB or WebDAV connection is made. A link whose target does not exist
    is refused, not followed.
  • Resume submitted and paid without the opt-in. learn resume passed
    allowIrreversible: true on every call. After a halt at assess, a plain resume clicked the
    next submit step and a cost step, and the receipt filed the submit as a witnessed automated
    submission. A resume now keeps the run's recorded mode, --submit takes only manual or
    witnessed-auto, and steps flagged cost or irreversible halt unless --allow-cost is given
    on the invocation that reaches them. The ledger entry of each step a grant allowed names it,
    and the receipt lists it (witnessedAutoSubmissions[].authorizedBy, authorizedCostSteps).
    The command line is read once, so a grant word given as the value of another flag, as in
    --attest "--allow-cost", is that flag's value and grants nothing.
  • Children of LEARN_*_CMD inherited the caller's folder and environment. With the documented
    python -m crucible, a crucible/ package in the folder where learn assist --crucible ran was
    executed. Children now start through the vendored safe spawn helper 1.0.1
    (src/_vendor/safe_spawn.mjs, pinned in VENDORED.sha256): absolute executable, private empty
    folder, environment allowlist extended only by LEARN_CHILD_ENV,
    NoDefaultCurrentDirectoryInExePath=1 on Windows, -P and PYTHONSAFEPATH=1 for Python.
    A command given as a bare name is looked up on PATH without any entry that reaches the folder
    learn runs in: an entry naming that folder or a folder below it, a junction or symlink to it,
    or a quoted spelling of it. The child's PATH leaves those entries out too, so a peer command
    that runs a helper by bare name does not find one planted there on PATH. When learn runs in a
    filesystem root, in your home folder or in a folder above it, it skips only an entry naming
    that folder itself. It never skips Node's own folder or the Windows, System32 and SysWOW64
    folders, and when it runs in one of them it skips no entry. On Windows a drive-relative name
    such as C:tool is refused. For the same reason LEARN_NATIVE_CONTROL must be an absolute
    path: a relative value imported a browser.mjs from the folder where learn run --native
    started.
  • The shipped docs/smoke.md no longer names a local development folder. The code default was
    already removed on main and ships here for the first time.

Breaking changes

  • State location. Sessions (tutor/) and runs (runs/) live in LEARN_HOME when it is set,
    else in %LOCALAPPDATA%\learn on Windows, ~/Library/Application Support/learn on macOS, and
    $XDG_DATA_HOME/learn or ~/.local/share/learn elsewhere. 1.6.0 wrote them into the folder the
    command started in. learn status prints the folder under state.
  • Resume. A plain learn resume no longer submits or pays. run --submit witnessed-auto no
    longer covers cost steps. A completed or denied run cannot be resumed.
  • MCP errors. A tool failure is a result with isError: true and structuredContent
    {code, retryable, setup, detail}, where code is INVALID_ARGUMENT, NOT_FOUND, CONFLICT or
    INTERNAL. 1.6.0 returned JSON-RPC -32000 errors with free text. An unknown tool is -32602.
  • No silent overwrite. learn_tutor_plan and learn tutor plan refuse to replace an existing
    session unless replace: true or --replace is given.
  • MCP paths. learn_dry_run takes workflow inline or workflowPath inside the state
    folder; packetPath and file resolve inside it too. learn_tutor_reverify names each
    receipt relative to the state folder (tutor/<id>.mastery.json), not by its absolute path.
  • Peer commands. A LEARN_*_CMD child sees only allowlisted variables and starts in a private
    folder, and a relative path in the command is refused. A peer command installed inside the
    folder learn runs in is no longer found by bare name, whether it sits in a project's
    node_modules/.bin or in a virtual environment inside that folder. With such a venv activated,
    python resolves to the next Python on PATH, so name the venv's interpreter by absolute path,
    for example ["/absolute/path/to/course/.venv/bin/python", "-m", "crucible"]. Each peer start
    reads every PATH entry, so a slow or unreachable network folder on PATH delays every start. The
    interop functions crucibleAssess, gatherRun and telosRender are async; they are not
    package exports.

Fixed

  • The learn bin works. src/cli.mjs starts with #!/usr/bin/env node, the repository checks
    out LF everywhere (.gitattributes), and main-module detection compares real paths, so the bins
    npm links run. In 1.6.0 the bin printed nothing on Windows and failed on Linux.
  • The human attestation records the real time of the resume. 1.6.0 recorded 1970-01-01.
  • MCP serverInfo.version reports the package version; 1.6.0 said 1.0.0. A test now holds
    package.json, package-lock.json, src/index.mjs, serverInfo, status, doctor, this file
    and the README to one version.

Added

  • learn mcp and a learn-mcp bin start the MCP server: npx -y @harperz9/learn@2.0.0 mcp.
  • --dir <folder> on the CLI for a project-local state folder, and LEARN_HOME for every entry
    point. learn status and learn_status report the folder in use under state, with its
    source: --dir, LEARN_HOME or default. LEARN_*_CMD also takes a JSON argv array, which
    keeps a path with spaces whole.
  • A release workflow. On a v* tag it checks the tag against every version site, smokes the packed
    tarball through npx on Windows, Linux and macOS, publishes with npm trusted publishing (npm
    records provenance), and creates a GitHub Release with the tarball and SHA256SUMS. CI runs the
    same tarball smoke on every push. Actions are pinned by commit SHA.
  • Also released from main for the first time: the repository art and its tests, and the
    src/interop.mjs organ-bundle entries (not exported and not imported by the package).

Moving from 1.6.0

  • To keep sessions and runs in a project folder, pass --dir <that folder> or set LEARN_HOME to
    it. To move them, copy the folder's tutor/ and runs/ into the folder learn status prints.
  • A run that should submit on resume: pass --submit witnessed-auto on run or on that resume. A
    run that should pay: pass --allow-cost on the invocation that reaches the payment step.
  • MCP clients that read JSON-RPC error text read structuredContent.code instead.
  • LEARN_TELOS_CMD="node ../telos/src/cli.mjs" becomes
    LEARN_TELOS_CMD='["node", "/absolute/path/to/telos/src/cli.mjs"]'. Name any variable a peer CLI
    needs in LEARN_CHILD_ENV, for example LEARN_CHILD_ENV=ANTHROPIC_API_KEY.