Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
7b9bb8c
docs: design multilingual ADR readiness work
SHcommit Aug 30, 2026
b69a784
docs: refine v0.2.0 readiness design
SHcommit Aug 30, 2026
9a86f5f
docs: align v0.2.0 work tracking
SHcommit Aug 30, 2026
7322380
feat: add repository locale configuration
SHcommit Aug 30, 2026
966b29b
feat: add eight-locale ADR rendering
SHcommit Aug 30, 2026
efc689a
feat: localize ADR repository initialization
SHcommit Aug 30, 2026
f6c1efb
feat: create localized ADRs with portable slugs
SHcommit Aug 30, 2026
bf347dd
feat: validate localized ADR workflows
SHcommit Aug 30, 2026
d691a33
fix: fail CHECK on incomplete git diff evidence
SHcommit Aug 30, 2026
801fa4c
fix: preserve both paths when CHECK sees renames
SHcommit Aug 30, 2026
dd96e12
fix: make CHECK honor git path semantics
SHcommit Aug 30, 2026
e6877d1
fix: reject conflicting CHECK diff modes
SHcommit Aug 30, 2026
503f5a6
docs: define CHECK evidence confidence
SHcommit Aug 30, 2026
477a738
docs: correct dogfooded ADR provenance
SHcommit Aug 30, 2026
f2f6c75
docs: supersede the MVP localization decision
SHcommit Aug 30, 2026
26141a1
docs: explain multilingual ADR workflows
SHcommit Aug 30, 2026
4588cbf
feat: ship v0.2.0 multilingual ADR support and release evidence
SHcommit Aug 30, 2026
b9e0ec3
fix: harden sync_version.py against silent drift and unicode loss
SHcommit Aug 30, 2026
baa0987
ci: run the manifest version-drift check once, not per matrix leg
SHcommit Aug 30, 2026
2f2a8de
refactor: centralize ADR directory loading into a shared iterator
SHcommit Aug 30, 2026
061d44e
docs: correct the Codex adapter's unverified symlink instruction
SHcommit Aug 30, 2026
5d37bef
fix: make SKILL.md the canonical source for manifest descriptions
SHcommit Aug 30, 2026
63c4c85
docs: formalize --json as a no-op, always-JSON CLI contract
SHcommit Aug 30, 2026
14627a4
docs: mark completed and re-scope enterprise-adoption's next candidates
SHcommit Aug 30, 2026
2c73af6
feat: promote CHECK's confidence classification to a stable field
SHcommit Aug 30, 2026
6e68b5d
feat: add a deterministic exception schema and wire it into CHECK
SHcommit Aug 30, 2026
ffc4f21
docs: bring handoff.md up to date with the P1 backlog work
SHcommit Aug 30, 2026
c995904
docs: close the Codex metadata compatibility item as not applicable
SHcommit Aug 30, 2026
b1a4ec5
chore: delete two stale remote branches, close the backlog item
SHcommit Aug 30, 2026
2fa6d67
docs: record ADR-0007..0010 for this session's design decisions
SHcommit Aug 30, 2026
d860a21
docs: bring handoff.md up to date with the branch rename and ADRs
SHcommit Aug 30, 2026
300456a
docs: write the ADR search and relationship navigation design spec
SHcommit Aug 30, 2026
f8b6423
docs: revise the ADR search/relationships spec per external review
SHcommit Aug 30, 2026
4db7fb8
docs: record the approved search/relationships spec in handoff.md
SHcommit Aug 30, 2026
20254cf
refactor: extract path_under into core/globs.py
SHcommit Aug 30, 2026
7c95692
feat: add core/query.py matching primitives
SHcommit Aug 30, 2026
ff25b68
feat: add deterministic rank_key to core/query.py
SHcommit Aug 30, 2026
92d95cc
docs: checkpoint handoff.md after tasks 1-3 of the search/relationshi…
SHcommit Aug 30, 2026
7ade27d
feat: add core/relationships.py canonical Relationship model
SHcommit Aug 30, 2026
c597a30
feat: add relationship integrity checks to core/relationships.py
SHcommit Aug 30, 2026
c8c2b06
fix: related.py keyword search now matches body, not just title
SHcommit Aug 30, 2026
5db2471
feat: add search command core filtering (keyword/tags/status/path)
SHcommit Aug 30, 2026
9a86666
feat: rank search results deterministically, add --limit/total/truncated
SHcommit Aug 30, 2026
36d7048
feat: wire search into the adr.py CLI
SHcommit Aug 30, 2026
954496a
docs: checkpoint handoff.md after tasks 1-9 (search command complete)
SHcommit Aug 30, 2026
2ed781e
feat: add Relationships section to the generated index
SHcommit Aug 30, 2026
38e0d27
feat: localize the Relationships section across all 8 catalogs
SHcommit Aug 30, 2026
b6d9658
feat: validate relationship integrity (supersedes/superseded_by)
SHcommit Aug 30, 2026
5e19e0a
docs: checkpoint handoff.md after tasks 10-12 (all code done, docs left)
SHcommit Aug 30, 2026
9694f87
docs: document the search command and related vs search framing
SHcommit Aug 30, 2026
68e2007
chore: regenerate docs/decisions/README.md with the Relationships sec…
SHcommit Aug 30, 2026
13accfa
docs: record ADR search/relationships feature as done
SHcommit Aug 30, 2026
8efde92
feat: detect supersession cycles as a validate error
SHcommit Aug 30, 2026
b8d93e4
feat: add --id exact lookup to the search command
SHcommit Aug 30, 2026
72427e1
docs: document cycle detection and --id lookup, checkpoint handoff.md
SHcommit Aug 30, 2026
22f76f5
docs: explain why ADR storage stays flat instead of sharded folders
SHcommit Aug 30, 2026
3ccd1e3
docs: record PR #2 opened, CI pending
SHcommit Aug 30, 2026
e58d8b1
fix: decode git subprocess output as UTF-8 explicitly, not locale def…
SHcommit Aug 30, 2026
105b776
docs: record PR #2 CI green and mergeable, spell out remaining approv…
SHcommit Aug 30, 2026
29b2e22
chore: bump version to 0.2.0
SHcommit Aug 30, 2026
9eccf61
docs: record version bump complete, CI re-verified green
SHcommit Aug 30, 2026
b841357
docs: consolidate completed work into changelog.md, trim tracking docs
SHcommit Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .adr-toolkit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schema_version": 1,
"locale": "en"
}
4 changes: 2 additions & 2 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "adr-toolkit",
"version": "0.1.0",
"description": "Initialize, record, and check Architecture Decision Records by inspecting the repository before asking questions."
"version": "0.2.0",
"description": "Initialize, record, and check Architecture Decision Records by inspecting the repository and existing decisions before asking questions."
}
10 changes: 9 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,13 @@ jobs:
run: pip install pytest
- name: Run tests
run: python -m pytest tests/unit tests/integration -v
- name: Check manifest versions are in sync

version-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Check manifest versions and descriptions are in sync
run: python scripts/sync_version.py --check
177 changes: 172 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,186 @@ of these adapters at it:
directly in a terminal — no agent required:

```bash
python skills/adr-toolkit/scripts/adr.py create --interactive --dir docs/decisions
python skills/adr-toolkit/scripts/adr.py create --interactive --dir docs/decisions --json
```

## Language and repository config

ADR Toolkit localizes deterministic, code-owned text in eight canonical
locales: `en`, `ko`, `ja`, `zh`, `fr`, `es`, `de`, and `pt-BR`. `zh` means
Simplified Chinese. User-authored prose is preserved as written, while JSON
keys, status values, error codes, IDs, and filenames remain machine-stable.

Choose the repository default during INIT:

```bash
python skills/adr-toolkit/scripts/adr.py init --locale ko --dir docs/decisions --json
```

This creates `.adr-toolkit.json` at the repository root:

```json
{
"schema_version": 1,
"locale": "ko"
}
```

Commands then use the repository default without repeating `--locale`:

```bash
python skills/adr-toolkit/scripts/adr.py create --interactive --dir docs/decisions --json
python skills/adr-toolkit/scripts/adr.py index --dir docs/decisions --json
```

An explicit flag overrides the repository default for one operation. Input
draft locale overrides the repository only when the CLI flag is absent:

```bash
python skills/adr-toolkit/scripts/adr.py create --locale ja --interactive --dir docs/decisions --json
python skills/adr-toolkit/scripts/adr.py index --locale fr --dir docs/decisions --json
```

The effective CLI order is explicit flag → approved input draft → repository
default → `en`. Unsupported locales and malformed config fail visibly.

### Unicode titles and portable filenames

Titles and bodies remain Unicode. Filenames remain ASCII for filesystem, URL,
and Git portability. For the title `결제 시스템 분리`, an agent can propose a
meaningful slug and show it for human approval:

```bash
python skills/adr-toolkit/scripts/adr.py create --input draft.json \
--slug separate-payment-system --dir docs/decisions --json
```

The deterministic core validates the slug and creates a filename such as
`0002-separate-payment-system.md`. It never translates or transliterates the
title itself. Without an approved slug or an ASCII fragment in the title, a
fresh repository safely falls back to `0001-decision.md`.

## CHECK confidence

CHECK does not certify the entire architecture. It evaluates only explicit,
structurally provable rules in the selected diff. Every finding carries a
`confidence` field with one of these four values directly — no need to
re-derive it from `kind`:

| `confidence` | `kind` it comes from | Meaning |
|---|---|---|
| `VERIFIED` | `related` | Applicable explicit rules were evaluated and none fired. |
| `VIOLATED` | `verified_violation` | Structural evidence confirms a violation. |
| `UNVERIFIABLE` | `review_required` or `no_applicable_constraint` | No usable rule vocabulary could prove or disprove this. |
| `NOT_APPLICABLE` | (no finding at all) | No known ADR/rule applies to the selected change — an empty `findings` list, not proof of compliance. |

Warnings mean some evidence could not be evaluated and must be reported. A
clean result never proves prose rationale, runtime behavior, or every
architecture invariant.

### Exceptions

`register_exception` — one of a Verified violation's five resolutions — is a
real, deterministic record, not just a label:

```bash
python skills/adr-toolkit/scripts/adr.py exception --input exception.json \
--dir docs/decisions --json
```

`exception.json` requires `adr_id`, `rule_id`, `owner`, `reason`, a `scope`
(path patterns the exception is narrowed to), and an `expiry`
(`YYYY-MM-DD`). The command assigns the next `EXC-NNNN` id and writes
`docs/decisions/exceptions/NNNN.json`. CHECK annotates a matching, non-expired
exception onto its finding's `exception` field — the finding's `kind` and
`confidence` stay exactly what the structural evidence says
(`verified_violation`/`VIOLATED`); an exception is visible, reviewable
evidence, never a silent pass. Once `expiry` passes, CHECK stops applying it
automatically.

## Search

Find an existing ADR by keyword (title **and** body), tags, status, or the
file path it governs — without opening every file:

```bash
python skills/adr-toolkit/scripts/adr.py search --keyword architecture --dir docs/decisions --json
```

```json
{
"ok": true,
"operation": "search",
"query": {"keyword": "architecture", "tags": null, "status": null, "path": null, "limit": null},
"count": 1,
"total": 1,
"truncated": false,
"results": [
{
"id": "ADR-0001",
"filename": "0001-record-architecture-decisions.md",
"path": "docs/decisions/0001-record-architecture-decisions.md",
"title": "Record architecture decisions",
"status": "accepted",
"tags": ["process"],
"matched_in": ["title", "body"]
}
],
"warnings": []
}
```

**Filter semantics:** filters across different fields (`--id`, `--keyword`,
`--tags`, `--status`, `--path`) are combined with AND. Multiple values within
`--tags` are combined with OR — `--tags postgres mysql` means "postgres or
mysql". No filters at all browses every ADR. `--id` looks up one ADR by its
exact id. `--path` matches a real file against an ADR's governed scope (the
same directory-boundary + glob logic CHECK uses), not an exact match against
the ADR's literal `affected_paths` list. `--limit N` truncates the
already-ranked (best-match-first) result list; `total` is always the
untruncated count and `truncated` is `total > count`.

`search` is a general lookup command ("has this been decided before?"), distinct
from `related` (used during RECORD's DISCOVER stage to find precedent for a
*new* draft, with a broader OR-across-fields match).

### Why storage stays flat

ADRs live as `NNNN-slug.md` in one flat `docs/decisions/` directory — no
per-year, per-team, or per-status subfolders — no matter how many
accumulate. Two comparable real-world tools were checked before deciding
this: [`npryce/adr-tools`](https://github.com/npryce/adr-tools), the
original Nygard-style CLI, ships no search at all and expects `grep`; the
most-adopted actively maintained ADR tool,
[`log4brains`](https://github.com/thomvaill/log4brains) (1.5k+ GitHub
stars), also keeps ADRs as flat Markdown and instead layers a full-text
search and a relationship graph on top — it does not shard the source
files by count. A folder hierarchy also creates a real problem flat storage
avoids: a decision that spans two teams or domains has no unambiguous
folder to live in.

The corollary is **retrieval, not storage, is where a growing ADR set gets
harder to use** — so that's where the effort went: `search`'s
title-and-body keyword/tag/status/path matching plus the generated index's
"By status" / "By tag" / "By affected path" / "Relationships" views. Both
already work identically for 10 ADRs or 500. Folder sharding, a rendered
relationship graph, and a real search index are deliberately not built yet
— they're tracked in [`project-roadmap.md`](project-roadmap.md), gated on
this repository (or an adopting team's) ADR count actually reaching a scale
where flat-directory substring search stops being fast enough. Building
that ahead of evidence would add real maintenance cost for a problem no one
has hit yet.

## Scope

- MADR 4.x format, no new standard.
- CHECK's MVP conflict detection is structural evidence only
(`constraints:` blocks) — no semantic/AST analysis. See
[ADR-0002](docs/decisions/0002-limit-check-s-conflict-detection-to-structural-evidence-only.md).
- Runtime text ships in English, French, Japanese, Korean, and Chinese —
scoped to `index`'s generated output only, since agent-composed prose
needs no lookup table. See
[ADR-0003](docs/decisions/0003-localize-only-index-py-s-generated-strings-not-agent-composed-text.md).
- Deterministic INIT, CREATE, and INDEX structure ships in eight locales;
agent-composed and user-authored prose is never machine-translated by the
core. See
[ADR-0006](docs/decisions/0006-localized-adr-generation.md).
- Everything explicitly deferred out of MVP is tracked in
[`project-roadmap.md`](project-roadmap.md), not silently dropped.

Expand Down
66 changes: 50 additions & 16 deletions adapters/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@ from a bare plugin directory. This repository is already a valid marketplace
root, because its `.claude-plugin/marketplace.json` is a manifest format
Codex CLI reads, so no extra manifest is needed.

1. Symlink this repo's `skills/adr-toolkit/` package under this adapter's
directory, as a sibling of `.codex-plugin/` (design spec §17.2):

```bash
mkdir -p adapters/codex/skills
ln -s "$(pwd)/skills/adr-toolkit" adapters/codex/skills/adr-toolkit
```

2. Register this repository as a Codex marketplace and install the plugin:
1. Register this repository as a Codex marketplace and install the plugin:

```bash
codex plugin marketplace add "$(pwd)"
Expand All @@ -35,21 +27,57 @@ Codex CLI reads, so no extra manifest is needed.
`.claude-plugin/marketplace.json`; `codex plugin marketplace add` prints it
on success, and `codex plugin marketplace list` shows it later.

3. Confirm Codex lists `adr-toolkit` as installed and enabled:
2. Confirm Codex lists `adr-toolkit` as installed and enabled:

```bash
codex plugin list
```

4. Optionally run
3. Optionally run
`python skills/adr-toolkit/scripts/adr.py preflight --json` from your
target repository to confirm the script layer works standalone.

The symlink is created at install time, not committed to this repo —
committing a real symlink breaks on Windows checkouts that don't have
`core.symlinks` enabled, which this project's CI can't assume. `.gitignore`
already ignores `adapters/codex/skills/adr-toolkit` so a `git add -A` after
step 1 cannot commit it by accident.
No symlink step is required. Registering the repository **root** as the
marketplace means Codex reads `.claude-plugin/plugin.json` as the plugin
manifest, and Agent Plugins' sibling-`skills/`-directory discovery finds the
real `skills/adr-toolkit/` package next to it directly — verified below by
installing with no `adapters/codex/skills/` symlink present at all.

## Why `.codex-plugin/plugin.json` and an adapter-local symlink exist here too

Design spec §17.2 originally planned every harness adapter as a
**self-contained** plugin directory: `adapters/codex/.codex-plugin/plugin.json`
as that harness's own manifest, with `adapters/codex/skills/adr-toolkit`
symlinked in as its required sibling `skills/` directory
(`ln -s ../../../skills/adr-toolkit adapters/codex/skills/adr-toolkit`), so
each adapter would work if pointed at directly.

That plan does not hold for Codex specifically: `codex plugin marketplace add`
rejects a bare single-plugin directory —

```
$ codex plugin marketplace add "$(pwd)/adapters/codex"
Error: invalid marketplace file `<repo-root>/adapters/codex`: marketplace root does not contain a supported manifest
```

— because only the repository root carries a marketplace manifest
(`.claude-plugin/marketplace.json`). So `adapters/codex/.codex-plugin/plugin.json`
and its sibling symlink are never read by the verified install path above;
they exist only for structural consistency with the Gemini and Antigravity
adapters' self-contained layout, and in case a future Codex version accepts a
bare plugin directory as a marketplace root. If you want that manifest to
resolve correctly on its own regardless, create the symlink manually:

```bash
mkdir -p adapters/codex/skills
ln -s "$(pwd)/skills/adr-toolkit" adapters/codex/skills/adr-toolkit
```

This is not committed to the repo — committing a real symlink breaks on
Windows checkouts that don't have `core.symlinks` enabled, which this
project's CI can't assume. `.gitignore` already ignores
`adapters/codex/skills/adr-toolkit` so a `git add -A` cannot commit it by
accident.

## Verification status

Expand Down Expand Up @@ -86,6 +114,12 @@ out of it against a scratch git repository, producing correct `"ok": true`
output for `preflight`, `init --dir docs/decisions`, and
`validate --dir docs/decisions`.

Re-verified with **no `adapters/codex/skills/` symlink present at all**: the
same three commands above still succeed and `preflight --json` still returns
`"ok": true` from the installed snapshot, confirming the symlink step this
README previously listed as install step 1 played no part in the working
flow.

Two details worth knowing:

- **A marketplace root, not a plugin directory, is what you register.**
Expand Down
2 changes: 1 addition & 1 deletion adapters/gemini-cli/gemini-extension.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "adr-toolkit",
"version": "0.1.0",
"version": "0.2.0",
"description": "Initialize, record, and check Architecture Decision Records by inspecting the repository and existing decisions before asking questions."
}
54 changes: 54 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,60 @@ Lightweight human-readable summary of meaningful repository changes.

## Unreleased

- Added `adr.py search`: finds an existing ADR by keyword (title **and**
body — `related`'s keyword match was title-only before), tags, status,
`--id`, or the real file path it governs, with deterministic best-match
ordering and `--limit`/`total`/`truncated` for bounding results. Added a
Relationships section (supersession chains + related lists, titles
alongside IDs) to the generated `docs/decisions/README.md`, localized
across all 8 catalogs, and `validate` checks for broken supersession
links, one-sided supersession edits, and supersession cycles.
- Added CHECK policy exceptions: `adr.py exception` validates and records a
schema-checked, owned, scoped, time-boxed exception; CHECK annotates a
matching finding with it but never suppresses or downgrades the finding —
an exception is visible evidence, not a silent pass.
- Promoted CHECK's violation classification to a stable `confidence` field
(`VERIFIED`/`VIOLATED`/`UNVERIFIABLE`) on every finding, instead of an
agent re-deriving it from documentation each run.
- Fixed a Windows-only bug where CHECK's git evidence gathering
(`diff.py`/`git_paths.py`) decoded subprocess output using the platform's
default locale encoding instead of UTF-8, garbling Unicode paths/content
on Windows CI while working correctly on macOS/Linux.
- Hardened `scripts/sync_version.py` against three silent-drift bugs
(a tracked manifest losing its version key, a manifest path outside the
repo root crashing instead of failing cleanly, non-ASCII manifest content
being escaped on write) and extended it to keep manifest `description`
fields in sync with `SKILL.md`'s frontmatter, the same way `version`
already was.
- Formalized `--json` as a documented no-op: every command has always
printed JSON regardless of the flag; `--help` now says so explicitly
instead of implying a choice that doesn't exist.
- Corrected repository ADR provenance and lifecycle: existing ADRs now name
an approved decision maker and locale, and ADR-0006 (repository-default
locale generation) supersedes ADR-0003 (index-only localization)
bidirectionally. This session's own design decisions were recorded as
ADR-0007..0010 (CHECK confidence field, exception mechanism, `--json`
contract, and a closed-as-not-applicable Codex tooling question).
- Bumped to v0.2.0.
- Added repository-configured deterministic ADR localization for eight locales
(`en`, `ko`, `ja`, `zh`, `fr`, `es`, `de`, `pt-BR`) across INIT, CREATE,
templates, prompts, and INDEX, with strict config/schema validation and
explicit override precedence.
- Added portable multilingual ADR creation: Unicode titles and bodies are
preserved, an approved semantic ASCII slug can be supplied, and non-ASCII
titles safely fall back to an ID-qualified `decision` filename.
- Hardened CHECK against false-clean results by failing closed on incomplete git
evidence, retaining both sides of renames and Unicode paths, detecting
working-tree deletions, exposing invalid ADRs, following Git ignore/path
semantics, and rejecting conflicting diff modes.
- Made relative ADR directories resolve consistently from `--root` for every
repository-scoped generation and validation command.
- Improved the repository's own ADR evidence and lifecycle history, including
the accepted ADR-0006 decision that supersedes the MVP index-only
localization decision ADR-0003.
- Expanded README, quickstart, and skill guidance with real multilingual
workflows and CHECK confidence boundaries, and added separate Korean v0.2.0
readiness and enterprise-adoption reports.
- Added the self-contained `skills/adr-toolkit/` package: deterministic
scaffolding/discovery commands (`init`, `discover`, `preflight`,
`validate`, `index`), ADR frontmatter/ID/lifecycle core logic, MADR
Expand Down
Loading
Loading