Skip to content

Releases: rvdbreemen/adr-kit

adr-kit v0.61.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 20:22
413b9d3

A minor release that brings ADR Kit natively to OMP (oh-my-pi), makes skills
answer in the user's language, and fixes a doubled OpenCode plugin. The
headline: an omp plugin install of ADR Kit now gets the same lifecycle
context, pull-request guard and judge host as the other clients, with no
manual configuration. ADR-050 accepts OMP as the fourth certified client; the
certification itself (registry entry, installer, doctor and native evidence)
follows in v0.62.0, and until then OMP is documented as native with
certification in progress.

Upgrading. Claude Code, Codex, Copilot CLI and OpenCode users need no
action. OMP users: omp plugin marketplace update rvdbreemen-adr-kit, then
omp plugin upgrade adr-kit@rvdbreemen-adr-kit, and start a new session so OMP
loads the extension. A hand-set ADR_KIT_ROOT is no longer needed and is
respected if kept.

Added

  • Native OMP (oh-my-pi) support (ADR-050, TASK-239). OMP installs the plugin
    from the existing marketplace (omp plugin install adr-kit@rvdbreemen-adr-kit) and loads omp/extension.ts, declared in
    package.json under omp.extensions. The extension maps OMP's session,
    prompt, edit, shell, subagent and compaction events onto
    hooks/adr-hook.py, blocks a shell call only on the pull-request guard's
    explicit deny, and sets ADR_KIT_ROOT so the skills find the engine, which
    OMP's missing ${CLAUDE_PLUGIN_ROOT} previously broke. omp-cli
    (omp -p --no-extensions --no-session --no-tools) joins the judge host
    clients, and an OMP session records it as the host. ADR-050 (Accepted)
    makes OMP the fourth certified client, amending ADR-010's client list; its
    registry entry, installer and doctor support and native evidence follow in
    v0.62.0, and until then OMP is documented as native with certification in
    progress (docs/clients/omp.md).
  • README: a Supported clients table separates the certified clients, the
    native OpenCode package, OMP's in-progress certification, and the portable
    MCP/Agent Skills fallback.

Changed

  • Skills and the ADR generator agent now reply in the language the user writes
    in, defaulting to English. Command output, identifiers, paths and ADR file
    content stay as they are: an ADR keeps the project's documentation language.
    A language set in the host client (for example Claude Code's language
    setting) still takes precedence (TASK-238).

Fixed

  • OpenCode: a global npm entry plus a project entry for a checkout loaded two
    plugin copies, since OpenCode dedupes by npm package name only. Each spawned
    its own hook per event, the skills path and guide were listed twice, and the
    global copy owned the MCP server and /adr-kit-* commands, so the checkout
    was not the code that ran. Now one copy per project is active, the
    last-loaded, which is the project's (TASK-235).

adr-kit v0.60.0

Choose a tag to compare

@github-actions github-actions released this 11 Oct 23:44
9a0b9b4

A minor release with two behaviour changes and the fixes of the 2026-10-11
defect sweep. The headline: the LLM layers now actually run on a default
install, on the coding assistant the plugin is installed in, and the user is
asked once whether to keep them. Before this release the common install route
(/plugin install) never recorded a host client, so the judge's LLM pass, the
adr-suggest nudge and the guardian's LLM tier silently did nothing.

What you will notice. The first session in a project records the client it
runs in as the judge host, and the next prompt asks, per layer, whether to keep
it on (recommended) or turn it off for this machine. The question repeats at
most once a day until it is answered. From that first session on, a commit's
diff goes to that client's own model: the same assistant that already sees the
code, with no API key and no pinned model. An "off" answer is written to the
gitignored .adr-kit.local.json and stays on your machine; it no longer
switches a layer off for the whole team. ADR-035 and ADR-049, which record
these defaults, are Accepted.

Upgrade notes.

  • To keep every diff away from any model, export ADR_KIT_NO_LLM=1. It now
    stops every model call, including the suggestion pass, which it did not
    before. If you relied on the nudge while it was set, the nudge is off too.
  • The pre-commit hook is a copy in each project. Re-run
    /adr-kit:install-hooks and choose replace to get the fixes that make a
    refused commit print its report and find the engine where Copilot CLI and
    OpenCode install it. The old hook is kept as a timestamped backup.
  • Run /adr-kit:upgrade to replace the project-scoped guardian entry, which
    no longer needs GNU sort -V. An entry added by an older
    /adr-kit:install-hooks has no version stamp: remove it with
    /adr-kit:install-hooks and add it again.
  • Codex runs a plugin's hooks only after you review them in /hooks;
    adr-doctor --deep now says so when they are unreviewed.
  • On macOS and Linux, OpenCode now finds a Python 3.10 or newer itself. If it
    reported the MCP server as failed, it should start after this upgrade.

Added

  • The LLM layers are on by default on every install route, on the coding
    assistant the plugin runs in, and the user confirms them (ADR-049). The
    layers - the per-commit judge pass, the adr-suggest nudge and the
    guardian's LLM tier - only run where a judge host client is recorded, and
    the common install route (/plugin install) never recorded one, so they
    did nothing. The first session in a project now records the client it runs
    in (never replacing a recorded choice), and the next prompt asks once whether
    to keep each layer on or turn it off for this machine, until the answer is
    recorded in llm.confirmed. /adr-kit:init and /adr-kit:upgrade ask the
    same, one question per layer, on every run.
  • OpenCode runs the LLM layers on its own model (opencode run --pure). Its
    plugin borrowed Claude Code's hook shape and would have recorded claude -p
    as an OpenCode user's judge; it now names its own host.
  • guardian.llm_enabled (default true) switches the guardian's LLM tier off;
    adr-substance then calls no model and the tier is never due.
  • adr-settings --set <switch>=<value> --local writes one of the four LLM
    switches for this machine only.
  • ADR-043 restores the two rules that guarded the host-model-only judge: no
    entry point may pin a vendor model, and none may carry its own default
    command vector. ADR-017 enforced them until ADR-036 superseded it without an
    Enforcement section, and since then no commit was checked against them. The
    scope now also covers bin/adr-substance, which calls a model and was never
    inside it; a gate test (adr-no-pinned-model-v1) fails when a new
    model-calling entry point is left outside the rule.
  • ADR-044 to ADR-047, from the 2026-10-11 defect sweep, are Accepted.
    • ADR-044 decides that every entry point finds Python and the engine through
      one resolver. Its Enforcement block applies from this release: a skill may
      not name a client's plugin-cache path or use sort -V. The skills comply
      (see Fixed); the resolver itself lands in a later release.
    • ADR-045 has the judge acquire its own diff and gives every diff shape an
      explicit verdict. ADR-046 has every reader of path_glob match in the
      judge's dialect. Both are implemented in a later release.
    • ADR-047 gives the query engine's cap on supporting ADRs an Accepted record
      again, as an amendment of ADR-036: at most two per response, filling only
      the slots under the limit. The engine already behaves this way. The cap was
      decided in ADR-014, which has been superseded since 2026-08-01.
  • ADR-048 is Accepted. It amends ADR-010: a native client is certified per
    platform, from a real run on that platform, and the documents claim only
    what a current record shows. Windows is no longer the only certifiable
    platform, and certification stays optional for a release. The validator,
    the support matrix and the new records land in a later release.

Changed

  • An LLM switch in the gitignored .adr-kit.local.json now wins over
    .adr-kit.json for judge.llm_enabled, suggest.enabled,
    guardian.llm_enabled and guardian.llm_autorun, so one developer's "off"
    no longer has to switch a layer off for the whole team. Only these four
    booleans cross over.
  • ADR_KIT_NO_LLM=1 now stops every model call, not only the judge's. The
    suggestion pass (adr-suggest, on by default) and the substance check
    (adr-substance) resolved their backend without looking at it, so a user who
    exported it to keep diffs off any model still had adr-suggest call one on
    every commit. The switch is now checked in the shared resolver and outranks
    --llm-cmd and ADR_KIT_LLM_CMD. If you set it and relied on the nudge, the
    nudge is now off too.
  • The adr-judge and adr-index-check workflow templates, and the readiness
    examples in the README and the grilling guide, pin the action to a release
    instead of @main, like the readiness template already did. They are
    registered version sites, so each release moves them.

Fixed

  • adr-readiness no longer suggests /adr-kit:grill --revalidate for an
    ADR that is consistently superseded, so finished records stop reading as
    open work. The supersession-required label stays (ADR-011 fixes the
    classes); the command is now suggested only where status and successor
    disagree (TASK-234).

  • On Windows, a host CLI installed through npm (Codex, Copilot CLI, OpenCode)
    is a .cmd shim that the LLM pass could not start (FileNotFoundError), so
    the pass degraded silently. The command is now resolved the way a shell
    would.

  • The guardian skill no longer says its LLM tier runs a pinned
    claude-sonnet-4-6; it runs on the recorded host's own model.

  • build-client-adapters.py and sync-agent-plugins.py no longer fail with
    ModuleNotFoundError in an installed plugin. client_certification.py
    imports client_support_matrix.py, which the release allowlist did not ship;
    a test now checks that every shipped script's sibling imports ship too.

  • The project guide installed as .claude/adr-kit-guide.md describes the edit
    tier as it works: the plugin's hook injects the governing ADRs before an edit
    and repeats the lookup after it, with inject.enabled / watch.enabled as
    the switches. It named bin/adr-watch, which no hook runs, and listed three
    of the seven MCP tools.

  • adr-suggest --help and the config schema give the suggestion pass's real
    default timeout, 30 s; both said 120.

  • adr-doctor --deep describes its probes as native registration, MCP and hook
    latency; it no longer mentions a model probe that was retired with ADR-036,
    and neither do the README and TROUBLESHOOTING.md.

  • The CLI latency corpus no longer budgets the retired adr-embed and
    adr-context-vector, budgets adr-watch instead of excluding it as a
    long-running watcher (every mode exits), and a test now rejects a budget
    for a name that is not in bin/.

  • clients/capabilities.json lists the three hook registration files as
    generated, which they are, instead of hand-authored.

  • The guardian's SessionStart entry (templates/cc-settings/guardian-hook-entry.json)
    no longer depends on GNU sort -V to pick the newest installed plugin. Where
    sort has no version mode the pipeline yielded nothing, and the entry's
    || true chain then skipped the guardian check on every session start
    without a word. The pick now runs on the Python the entry already finds.
    The same class of defect as #155; existing projects pick it up through
    /adr-kit:upgrade, which replaces the entry.

  • The ten skills that run a bundled tool find the kit wherever it is
    installed. In Claude Code that includes a --plugin-dir checkout, and the
    fix also covers OpenCode, which loads the same skills. They looked only in
    Claude's marketplace cache, through a pipeline that needs GNU sort -V. Now
    they take ADR_KIT_ROOT when it is set, which the OpenCode plugin does for
    its shell, and otherwise the plugin root that Claude Code substitutes into
    the skill (ADR-044). When neither yields a root, the skill stops with one
    line saying so.

  • /adr-kit:install-hooks adds the project-scoped guardian hook from
    templates/cc-settings/guardian-hook-entry.json instead of an inline copy.
    The copy still used sort -V, which the template no longer does (see the
    entry above), and it had no _wrapper_version stamp, so the guardian
    reported it as present but never as stale and /adr-kit:upgrade left it in
    place. To replace an entry added with the copy, remove the guardian hook with
    /adr-kit:install-hooks and add it again.

  • A commit the judge refuses prints the violation report. Wherever flock
    exists, the hook's concurrency lock sent its stderr to /dev/null for the
    rest of the hook, so th...

Read more

adr-kit v0.59.1

Choose a tag to compare

@github-actions github-actions released this 05 Oct 22:19
9bfe58d

A patch release for the pre-commit hook, prompted by an external bug report
(#155, thank you @Simwawa). Both fixes make the hook choose the right tools on
machines that do not look like the maintainer's: several Pythons side by side,
or a sort without GNU extensions such as the one macOS ships.

Upgrade note. Nothing needs to change on a supported setup (a Python 3.10 or
newer on PATH). On a machine whose only Python is older than 3.10, the hook no
longer runs adr-judge on that interpreter. It prints its existing
"Python 3 not found ... Skipping ADR check for this commit." message and lets
the commit through. Install Python 3.10+ to get the check back.

The hook is a copy in each project, so existing projects pick the fix up by
re-running /adr-kit:install-hooks and choosing replace when it reports
that .githooks/pre-commit differs from the template. The old hook is kept as
a timestamped backup.

Fixed

  • The pre-commit hook selects a Python that can run adr-judge. Its probe
    took the first command reporting any Python 3, so with python3 on 3.9 and
    a newer python or py installed it picked 3.9 and the gate failed. It now
    asks each candidate for its version and skips one below 3.10. Reported in
    #155.
  • The pre-commit hook no longer depends on GNU sort -V to pick the newest
    installed plugin. Under set -e a sort without version mode aborted the
    whole hook; the comparison now runs on the Python the hook already requires.
    Reported in #155.

adr-kit v0.59.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 21:13
7857f42

A follow-up to 0.58.0 that finishes the same theme: tools that measured the
developer's machine instead of the project, and tools that quietly rewrote
files they should only have read.

Upgrade notes. Nothing needs to change, but two results can differ:

  • adr-retire now reads the files git versions (tracked, plus untracked
    files that are not ignored). A technology used only in git-ignored output
    no longer counts as in use, so an ADR whose only remaining trace was build
    output may now appear as a retirement candidate. That is the intended
    reading; check the candidate before retiring it.
  • /adr-kit:lint now runs bin/adr-lint, so the first run may ask your
    client for permission to use the shell.

Fixed

  • adr-retire scans what git would version, not what lies around. It walked
    every directory, so git-ignored output such as graphify-out/ (1174 files,
    67 MB on one machine) took a run from under a second to 3.3 s, and CI,
    where that folder does not exist, never saw it. Inside a git repository it
    now reads git ls-files --cached --others --exclude-standard; outside one,
    its walk skips junctions and directory symlinks and stops after 100,000
    entries or 10 seconds, as adr-lint's scan does since 0.58.0 (TASK-212).
  • scripts/release.py can be re-run after prepare, as its own message
    says. preflight refused the uncommitted version bump that prepare leaves
    behind on purpose; it now accepts a tree whose only changes are declared
    version sites and the generated client trees, once the version is
    written, and still names any other changed file (TASK-213).
  • adr-doctor no longer regenerates client adapters when it runs from a git
    checkout; it reports them stale with the build command instead. Repair
    mode rewrote this repository's codex/ and copilot/ from every test
    that ran doctor without --check. Installed payloads keep the repair
    (TASK-213).
  • bin/adr lifecycle commands keep a CRLF file CRLF. They wrote every
    record back with LF, so one answered question showed as a whole-file
    change on a Windows checkout (TASK-213).
  • /adr-kit:lint runs bin/adr-lint and explains its findings. It
    described the CLI's output without being allowed to run it (TASK-213).
  • This repository's own .adr-kit/ADR-guide.md and .githooks/pre-commit
    are refreshed from their sources, the guide's stamp is a declared version
    site, and a test keeps both copies equal to their source (TASK-213).

adr-kit v0.58.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 19:46
91cf41a

This release is about tools that could run for a very long time and checks
that could stay green while checking nothing. adr-lint no longer walks into
Windows junctions without bound, and the judge's LLM pass now has a ceiling a
host CLI cannot hold open. Two new advisory checks look for what every gate
missed: Enforcement rules whose scope reaches no file, and ADR sections that
are written but say nothing.

Upgrade notes. Nothing needs to change for an existing project. Three
behaviours are new and visible:

  • judge.llm_pass_timeout_seconds (default 600) is a new, optional key in
    docs/adr/.adr-kit.json. A pass that reaches it leaves the remaining ADRs
    without a verdict and marks the attestation degraded; the commit still
    passes on the declarative checks.
  • adr-judge-precommit exits 2 after 900 s (ADR_KIT_JUDGE_TIMEOUT_S)
    where it used to wait without limit.
  • adr-lint reports a gate it could not reach within its scan budget as
    could not be verified rather than as missing. Pass --repo-root the
    project root if you see it.

Added

  • adr-substance asks the host model, in the guardian's LLM tier, whether
    each written section of a Proposed ADR actually answers its heading. It
    reports "TBD", "see above" and generic prose, which every deterministic
    check reads as written, and quotes the author's own words. A quote the
    model cannot locate in the section is dropped. One isolated call per
    Proposed ADR; --estimate states the count before anything is spent, and
    the guardian skill adds it to its cost prompt. Advisory only: the exit code
    is 0, and adr-readiness, the MCP server and adr-lint are untouched
    (TASK-203).
  • adr-judge --check-scope names every Enforcement rule of an Accepted ADR
    whose path_glob matches no tracked file. Such a rule is checked against
    nothing, so every gate that asks whether it was violated answers green for
    as long as the glob and the code disagree about where the code lives.
    adr-audit --whole-codebase carries the result as advisory, without moving
    its exit code. The review skill gains the half no tool can check: whether
    every case an ADR enumerates and every target it names reaches an
    implementation (TASK-204).

Fixed

  • adr-migrate names a Related Decisions section it had to add. The
    conversion writes - None. into it, which every detector rightly reads as
    content, but the author never saw the section. It is now listed as
    review: ## Related Decisions, apart from the placeholder holes, and a
    hand-written - None. is still left alone (TASK-202).
  • adr-migrate --to-profile no longer blames a conversion for holes the
    source already had. It compared section titles before and after, and two
    profiles name the same section differently, so a nygard ## Context that
    already held a TODO was reported as a new ## Context and Problem Statement hole on the way to madr. It now compares roles, each side read
    in its own profile (TASK-201).
  • The judge's LLM pass now has a ceiling it keeps. A host CLI call used to
    return only when every process holding its output pipe had exited, so a
    helper it started could hold a 120 s call open indefinitely: measured at
    60 s for a 2 s timeout. Output now goes to temporary files and a timeout
    stops the CLI's whole process tree. The pass as a whole stops after
    judge.llm_pass_timeout_seconds (default 600); ADRs not reached get no
    verdict and the attestation names them, as for any unusable call
    (ADR-038). adr-judge-precommit gives up after 900 s, configurable with
    ADR_KIT_JUDGE_TIMEOUT_S, and exits 2 with a hint (TASK-210).
  • adr-lint can no longer walk without end while looking for an Accepted
    binding ADR's gate. The scan descended into Windows junctions, which
    os.walk(followlinks=False) does not treat as links, and its 5000-file cap
    counted only files it read. A junction into a large tree took 160 s where it
    now takes 0.5 s. The scan now skips directory links and graphify-out,
    skips files over 1 MB, and stops after 100,000 entries or 10 seconds. When a
    budget runs out the finding says the gate could not be verified and names
    the budget, instead of reporting the gate as missing (TASK-209).
  • bin/adr accept no longer waits forever on the quality and lint runs it
    starts: they time out after 120 s and report the timeout as an error. The
    git calls in adr-lint no longer inherit the caller's stdin.
  • The guardian's Proposed-ADR queue ranks by age again. Every record showed
    age 0 days, because adr-readiness dropped the date the ADR catalog had
    already parsed. Each readiness item now carries date: the latest status
    change, or the frontmatter date when there is no history. The report stays
    deterministic, since the date comes from the file and not from the clock
    (TASK-200).
  • An Open Question that wraps onto more lines, or carries nested bullets, is
    one question again. bin/adr answer used to mark only its first line, so
    the answer landed mid-question; --question <text> searched only that
    line; each nested bullet counted as a question; and a continuation line
    ending in ? became an unresolved question that answer could not reach,
    which kept accept blocked after everything was answered. All three
    parsers now share one item grouping. A multi-line question gets its answer
    on a new line after its last line, and the confirmation no longer cuts the
    question mid-word (TASK-194).
  • The ADR guide that setup installs as .adr-kit/ADR-guide.md carried the
    stamp v0.35.0 in every project set up since v0.36.0. Its source is now a
    declared version site and moves with each release (TASK-192).
  • The test suite no longer renames a developer's live Copilot plugin
    directory while testing the installer (TASK-191), and the certification
    checks no longer fail on the calendar 30 days after their fixture date
    (TASK-211).

adr-kit v0.57.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 04:45
867a75b

This release makes two gates stricter, and both can turn a pipeline that was
green on 0.56.0 red on upgrade. adr-lint --strict and bin/adr accept now
fail a required section that is present but empty, reporting
missing sections: ['References (present but empty)']; fill the section, and
note that a migration placeholder still passes deliberately, so an imported
record is never blocked on arrival. The lifecycle commands now exit 2 rather
than 0 when an ADR's Status line records a transition they would otherwise
discard; write the ## Status History block and re-run.

Everything else is a release the project can now run on itself. One command
drives it end to end, the tag is created from the merge rather than typed, and
adr-readiness says when a record holds a placeholder instead of an answer
rather than reporting it ready.

Added

  • One command now drives a release. python scripts/release.py X.Y.Z runs every
    step of docs/RELEASING.md in order and is safe to re-run: each phase asks the
    repository whether its work is already done, so an interrupted release is
    resumed rather than restarted. --status reports what is left without changing
    anything, --only runs a single phase, and --skip-tests leaves the suite to
    CI. docs/RELEASING.md remains the specification the driver implements; the
    driver is now the sanctioned path (ADR-042).

  • An installation smoke workflow. It exercises the .pre-commit-hooks.yaml
    install path through pre-commit try-repo against a fixture repository, which
    nothing tested before, and checks that every @vX.Y.Z action pin shipped in
    the README and the templates names a tag that exists and still defines that
    action. Its job summary states what it does not cover: the three vendor CLIs
    are not installable on a hosted runner and stay certified from retained
    Windows evidence (ADR-010).

  • A gate refusing a CHANGELOG entry appended to a section that has already
    shipped. release-publish.yml publishes a ## [X.Y.Z] section verbatim as
    its GitHub Release body and section membership is positional, so a bullet
    written under a released heading is absent from the next release's notes and
    claimed by a release that does not contain it. That reached dev twice before
    a human sweep found it, because the diff carries ### Fixed as unchanged
    context and reads like a correct addition. The check compares the number of
    top-level bullets against the same section at its tag rather than the text:
    measured over the 70 sections that can be compared, several differ from their
    tag today and every one is a correction, which a byte-identity rule would
    fail. Both tag spellings this repository ships are resolved, v0.56.0 and the
    older adr-kit--v0.1.0, so only six sections cannot be compared at all, and
    those are reported as skipped rather than passed. It runs in validate, the
    only required check on dev.

Changed

  • The driver recognises an unwritten release section by its shape rather than by
    the word TODO appearing anywhere in it. bump-version.py inserts a
    - TODO: describe this release. list item and the driver must refuse to
    publish that as a Release body, but the substring test also refused this very
    release, whose notes describe placeholder detection and therefore quote
    - TODO: markers five times. A gate that will not let a release describe its
    own subject leaves the author no move except worse notes. A leftover TODO list
    item is still caught, which comparing against the exact scaffold string would
    have missed.
  • The driver now exits non-zero when npm serves a different version than the one
    released. npm sets latest to the version published last rather than the
    highest, so approving staged versions out of order silently points
    npm install at an older release; that left 0.55.1 released while latest
    read 0.54.0 for a week with nothing reporting it. A registry that does not
    answer is reported as its own state rather than guessed at, and also exits
    non-zero, so a release is never reported as finished on the strength of a
    failed lookup.
  • The release no longer asks anyone to type a tag. release-publish.yml creates
    it from the merged commit, and the driver verifies the tag resolves to
    origin/main and refuses to move one that does not. Typing the tag by hand is
    what cost v0.55.0.
  • The local install step reads each client's version back instead of trusting the
    installer's exit code, which has reported success while leaving clients on the
    previous version.

Fixed

  • Lifecycle commands refuse to discard a transition the Status line still
    carries. On an ADR that predates the ## Status History convention, accept,
    reject, propose, supersede and document seed one recovered entry from
    the Status line before replacing it, but the reader took only the leading
    status and the first date. A line such as Superseded by ADR-088, 2026-08-07. Originally Accepted, 2026-05-08. yielded exactly the transition a supersede
    repair was about to write, the equality check returned early, and the line was
    overwritten with exit 0: the history then asserted the record's first-ever
    transition was Superseded. The command now exits 2, naming the file, the line,
    what it kept and what it would lose, and changes nothing; write the
    ## Status History block first, then re-run. The recovered entry's reason
    also carries the Status line verbatim, so a Decision Maker: attribution or
    any other prose the parser has no field for survives instead of being dropped.
    templates/adr-kit-guide.md documents both refusal conditions.
    (#120)
  • A required section that is present but empty no longer counts as complete. The
    completeness gate tested whether the heading matched somewhere in the text and
    never looked at the body, so ## References with nothing under it passed.
    bin/adr accept runs that gate with --strict, which made an empty heading
    enough to walk a record carrying no verifiable reference into an immutable
    Accepted state. The finding now separates the two cases, References against
    References (present but empty), because an author needs to know whether the
    section is missing or hollow. The rule stops at empty, deliberately: a
    migration placeholder still counts as content, because an imported record must
    not fail a blocking gate on arrival. The same hole existed in adr-quality,
    whose section loop scored presence while three checks beside it already
    measured emptiness for Decision, Alternatives and Consequences; References and
    Related Decisions fell through that gap and
    adr accept --quality-threshold reads that score.
  • adr-migrate says which sections it could not fill. Converting a profile
    appends - TODO: ... into any required heading it has to add, which is honest
    at write time but counts as content by decision, so a migrated record passes
    completeness and nothing said it was unfinished. Each such section is now
    reported as needs content: ## <heading>, counting only what that run left
    unfilled so a hole the author already had is not blamed on the migration. That
    report is the whole signal, and deliberately not a refusal: the gates stay open
    on a placeholder, which is what keeps a migrating team from disabling them.
    Measured on a real record whose ## References holds only
    - TODO: add verifiable references., the acceptance gate set still passes it
    (adr-quality scores it 0.87, grade A), while the same record with an empty
    ## References is blocked with
    missing sections: ['References (present but empty)'].
  • adr-readiness says when a required section holds a migration placeholder
    rather than an answer. A Proposed record whose ## References contained only
    the line bin/adr-migrate writes classified ready-for-confirmation and
    carried no finding at all, which is a lie about a record
    nobody has finished writing; nothing between the migration and adr accept
    ever said otherwise, because the migrator prints its report once, to whoever
    ran the command. The new SECTION_PLACEHOLDER_ONLY finding names each
    section and moves the record to needs-human-input, which gives it a
    next_command of /adr-kit:grill. Readiness cannot block and deliberately
    gains no exit-1 path: the completeness gate still passes such a record on
    arrival, because a team that hits a wall on import disables the gate.
    Empty sections are not reported here, since adr-lint already fails
    completeness on those. Measured across 212 real records (this repository's 43
    and the 169-record OTGW corpus): zero new findings, so the signal fires only
    on records that carry a placeholder.
  • The guardian queue no longer drops a record for being honest about itself.
    Enrollment is gated on signals = (linked, shipped, ready, open_questions, below_threshold), and for exactly this population -- unlinked, unshipped,
    quality above the threshold, no open questions -- ready was the only true
    signal. Reporting the placeholder moves the classification off
    ready-for-confirmation, which measured as 0 candidates: the honest report
    would have made the record less visible than saying nothing. A needs_human
    signal now carries it, with its reason placed before the unconditional
    age N days entry so it survives the reasons[:2] cut the SessionStart
    block applies.
  • unfilled_required_sections recognises both placeholder spellings. It matched
    the - TODO: list item bin/adr-migrate writes but not the
    <!-- TODO: ... --> comment the /adr-kit:migrate skill writes, while
    bin/adr-lint has always stripped comments before counting and
    tests/test_adr_policy.py pins the two as equivalent. A skill-migrated record
    was therefore reported as finished. The helper also splits the two holes it
    used to conflate: placeholder_required_sections ...
Read more

adr-kit v0.56.0

Choose a tag to compare

@github-actions github-actions released this 27 Aug 05:25
0548ed5

The release now tags itself. release-publish.yml reads the CHANGELOG version
on a push to main and creates the tag on the commit that carries it, so a tag
can no longer name a commit whose version sites disagree with it. That is the
failure that burned v0.55.0. Nothing to do on upgrade.

Two lifecycle defects are fixed that both ended in a decision silently not
reaching your agents, with exit code 0 throughout.

Added

  • release-publish.yml creates the release tag from the merged main commit
    and publishes in the same run, and its job summary now carries the npm
    approval steps, the staged-packages URL, and the warning that npm sets
    latest to the version published last. The instruction used to live only in
    docs/RELEASING.md, which is why three staged versions sat unapproved for a
    week. Recorded in
    ADR-042,
    which amends ADR-012's automation boundary.
  • scripts/check-release-version.py --print-canonical prints the canonical
    CHANGELOG version and nothing else, so the workflow derives the tag from the
    same registry entry the gate compares against.
  • tests/test_docs_claims.py holds four documentation claims that nothing read
    before: the counts the README advertises, no version literal in
    SECURITY.md's supported-versions section, no current-version assertion in
    ROADMAP.md's status section, and every action pin in a file users copy must
    be a declared version site.
  • adr_format.section_span() returns the offsets section_text() already
    computed, so a writer can place content inside the bounds the reader parses.

Fixed

  • Lifecycle commands no longer write status_history entries outside the
    ## Status History section. append_status_history located its insertion
    point with body.find("```") over the whole remaining document, so on a
    record whose history block carries no fence - the shape the shipped agent
    template emits - the entry landed in the next fenced block, in this project
    the ## Enforcement JSON. The frontmatter read Accepted while
    ADR-INDEX.json went on reporting Proposed, and only Accepted ADRs are
    injected, so a decision the maintainer had just signed stopped reaching any
    agent. A second shape, no later fence at all, appended a second
    ## Status History section and inverted chronology. Affects accept,
    reject, propose, supersede and document, which share one mutation
    path. (#119)
  • Frontmatter inference reads the status instead of defaulting to Proposed.
    It recognised exactly one Status shape and guessed on every other, including
    any line starting with **, [ or -. adr-migrate wrote that guess and
    exited 0. Measured over a real 169-record corpus, 79 records were affected:
    60 read Proposed while the body said Accepted, 12 while it said
    Superseded, one Amended. The reader that handles those shapes already
    existed and called itself the single cross-tool status reader; the function
    that writes the field never used it. It now does, and an unreadable status
    stays undetermined rather than becoming a confident wrong answer - both
    callers already had an honest path for that and neither was being used.
    (#118)
  • superseded_by survives the link-wrapped form real bodies use,
    Superseded by [ADR-124](ADR-124-title.md), and the date and successor are
    now recovered from the same line the status word was read from rather than
    from a ## Status section the record may not have.
  • Lifecycle commands no longer feed the generated ADR-INDEX.md to the
    frontmatter loader. glob("ADR-*.md") matches it, which was only ever
    harmless because inference invented a status for it.
  • SECURITY.md no longer names a supported version line that goes stale; it
    states the policy and points at the latest release. It had claimed v0.33.x
    was current, twenty-two minor versions behind, and contradicted the sentence
    directly above it.
  • The README described the MCP server as five-tool in five places while saying
    "Seven tools" further down, claimed 15 workflows against 17 and six bounded
    hooks against eight, and pointed OpenCode users at a superseded package
    version. ROADMAP.md asserted a version number and omitted the OpenCode
    distribution.
  • templates/github-workflows/adr-readiness.yml pinned its action eighteen
    minor versions back because the pin was declared in no registry. It and the
    README OpenCode package pin are now sites in
    packaging/version-sites.json, so scripts/bump-version.py writes them.

Changed

  • The Claude Code marketplace listing no longer advertises "v0.33 local
    governance tools" as if that set were new.
  • The reviewed migration baseline for the bundled corpus moves from 81
    deterministic and 88 guided to 79 and 90, and the metadata dry run from 154
    changed and 15 failed to 152 and 17. Two records now need human judgement
    instead of receiving an invented status, and two more report an actionable
    issue instead of being rewritten with one. The retrieval probe for PS=1
    summary parsing now expects ADR-046 rather than its superseded predecessor
    ADR-045, which ranked as the governing answer only because its status was
    misread.

adr-kit v0.55.1

Choose a tag to compare

@github-actions github-actions released this 26 Aug 05:15
3b5ca00

The version number skips 0.55.0. That tag was pushed at the dev tip before
the version bump was committed, so every version site at that commit still
read 0.54.0 and the release gate correctly refused to publish it. The tag is
left in place rather than moved, because a pushed tag is a public ref that
consumers may register a marketplace from. Nothing was ever published as
0.55.0.

ADR-029 is finally carried out: the native hook binary is gone and Python is
the only hook host on every platform. Nothing to do on upgrade unless you set
ADR_KIT_NATIVE_HOOK, which no longer exists.

Removed

  • The committed adr-hook.exe artefact, its Rust sources, the dispatcher
    branch that preferred it, and the ADR_KIT_NATIVE_HOOK opt-in, from all
    three generated client trees. ADR-029 has been Accepted and binding: true
    since 2026-08-04 and its Must reads "Remove the native host, its committed
    artefact, and the dispatcher branch that prefers it"; none of it had
    happened (TASK-187).
  • Three parity-from-source tests in test_adr_hook_result_limit.py that read
    the Rust source to compare it against the Python host. Maintaining that
    comparison is the second implementation ADR-029 rejects.

Fixed

  • adr-mcp rejects malformed JSON-RPC frames instead of answering them
    (TASK-186). Four deviations, all reproduced against v0.54.0:

    • "id": null was answered with a full result and "id": null, so the
      reply itself failed to validate against JSONRPCResultResponse. The base
      protocol says the ID MUST NOT be null; only key presence was checked.
    • "id": 1.5 and "id": {"a":1} were echoed verbatim. RequestId is
      string | number.
    • The jsonrpc member was never checked, so a frame with no version, or
      with "1.0", was served normally and got "2.0" back.
    • io.modelcontextprotocol/clientCapabilities was tested for presence only,
      so the string "nonsense" passed and server/discover succeeded. A
      schema-invalid params now returns -32602.

    None of these breaks a conforming client. They are worth fixing because a
    spec deviation in an outgoing frame is the one category the other side
    cannot correct, and a non-conforming client previously received a
    valid-looking answer instead of a usable error.

  • ADR-029's gate no longer certifies the option the decision rejects. The
    anchor flipped on by TASK-127 asserted that the native host ran only under
    ADR_KIT_NATIVE_HOOK=1, which is the opt-in state ADR-029's Decision
    Outcome rules out, so adr-lint reported compliance for a decision nobody
    had implemented. The Verification section is corrected in place, dated, and
    carries a matching status_history entry; verified_in now names the three
    tests that hold the assertions.

  • bin/adr_doctor_checks.py no longer requires adr-hook.exe, which would
    have made every healthy install report "failed" once the artefact was gone.

Changed

  • docs/hook-performance.md describes Python as the only host rather than a
    fallback, and drops the edit-tier budget that ADR-030 had already replaced.
  • The ADR-INDEX.json size budget in tests/test_adr_index.py allows 2.5 KB
    per ADR instead of 2 KB. At 41 records the old bound cleared by 4 bytes, so
    it had stopped measuring growth and fired on whichever ADR was edited next.
    The ratio assertion that bounds the graph at 25% of the ADR markdown is
    unchanged and remains the meaningful limit.

adr-kit v0.54.0

Choose a tag to compare

@github-actions github-actions released this 20 Aug 19:57
1f4cc11

Automatic interactive ADR grilling is now enabled by default for user-visible
sessions. No breaking changes; existing projects retain an explicit opt-out.

Added

  • Automatic Proposed-ADR handoff (ADR-041): the next user-visible prompt
    can emit one bounded AUTO_GRILL_PENDING instruction for the highest-priority
    unfinished Proposed ADR. The handoff remains deterministic, local, fail-open,
    and human-gated.
  • Settings visibility: grill.auto_start is available through
    bin/adr-settings, with provenance showing whether the default or project
    configuration supplied the value.

Changed

  • Upgrade default materialization: /adr-kit:upgrade writes
    grill.auto_start: true when an existing project has no explicit value. An
    existing grill.auto_start: false setting is preserved.
  • Native client handoff: Claude, Codex, Copilot, and OpenCode receive the
    exact client-native grill command without starting interviews from CI,
    pre-commit, background, or unattended lifecycle events.

Upgrade Notes

  • Run /adr-kit:upgrade after installing this release to materialize the new
    project setting. The runtime default is already enabled when the setting is
    absent.
  • Set grill.auto_start: false for a project-wide advisory-only opt-out, or
    use ADR_KIT_AUTO_GRILL_DISABLE=1 for one run.

adr-kit v0.53.0

Choose a tag to compare

@github-actions github-actions released this 25 Aug 22:37
be8f839

The MCP server grows from five to seven tools, completing the prescribed
agent development cycle for MCP-only clients. No breaking changes; existing
installations need no migration.

Added

  • adr_lint MCP tool: lint the ADR set against the deterministic
    verification gates over MCP, with an optional strict mode. Wraps
    bin/adr-lint --format json and reports verdict: ok or
    verdict: findings alongside the full JSON report, so a shell-less agent
    can run the mandatory pre-completion lint step.
  • adr_related MCP tool: the dependency graph (inbound and outbound
    edges, supersession links, dangling references) for one ADR over MCP,
    wrapping bin/adr-related --format json. Agents can now follow graph
    links after adr_context without raw file reads.
  • ADR-040 (Accepted): records the admission criterion for the MCP tool
    surface — read-only, deterministic and key-free, agent-query-shaped, and
    covering a development-cycle step no exposed tool already covers. Future
    tool proposals are evaluated against these four recorded tests.

Changed

  • Agent guide rewritten for autonomous operation
    (.adr-kit/ADR-guide.md): a tool-surface table mapping each MCP tool to
    its CLI equivalent with mutation flags, per-phase steps with expected
    outcomes and agent reactions, and an explicit human-gated list (accept,
    supersede, reject, signer). Projects pick this up on the next
    /adr-kit:setup or /adr-kit:upgrade.

  • Doctor deep probe and installer smoke test now verify the complete
    seven-tool MCP contract.

  • OpenCode npm publication flow now runs from the canonical
    release-publish.yml workflow. It stages the package through Trusted
    Publishing after the GitHub Release, while maintainer 2FA approval remains
    required before the package becomes public.

  • OpenCode npm publication flow now runs from the canonical
    release-publish.yml workflow. It stages the package through Trusted
    Publishing after the GitHub Release, while maintainer 2FA approval remains
    required before the package becomes public.