Repository navigation
feat(cli): add adr accept and a terminal view for adr queue (ADR-0044) - #250
Conversation
Decisions governing this change
Active proposals touching this changeThese are not yet ratified and do not bind this change:
Historical records that once covered this changeThese no longer bind this change, and are listed for context only:
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The terminal queue can recommend an acceptance that must fail, and successful acceptance prints unsanitized corpus-controlled terminal text.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Adds human-driven ADR acceptance and a TTY-aware queue view under proposed ADR-0044.
Changes:
- Adds the pure
acceptAdrSourcetransition andadr acceptCLI command. - Adds terminal queue rendering while preserving piped Markdown output.
- Updates tests, documentation, command registration, and ADR inventory.
Decision-context MCP tooling was unavailable, so governance applicability beyond supplied context was not independently verified.
| File | Description |
|---|---|
AGENTS.md |
Documents queue and acceptance behavior. |
CHANGELOG.md |
Records the new CLI features. |
MANIFEST.md |
Adds ADR-0044 to generated inventory. |
docs/adr/0044-...md |
Proposes the governing decision. |
site/src/content/docs/commands.mdx |
Documents public CLI usage. |
scripts/emit-manifest.ts |
Updates write-surface commentary. |
packages/core/src/index.ts |
Exports the transition API. |
packages/core/src/transition/accept.ts |
Implements splice-and-verify acceptance. |
packages/core/test/surface.test.ts |
Verifies the public export. |
packages/core/test/transition-accept.test.ts |
Tests transition behavior and refusals. |
packages/cli/src/accept.ts |
Implements adr accept. |
packages/cli/src/command-registry.ts |
Registers command and format choices. |
packages/cli/src/graph.ts |
Uses shared terminal helpers. |
packages/cli/src/index.ts |
Dispatches the new command. |
packages/cli/src/queue-terminal.ts |
Renders the terminal queue. |
packages/cli/src/queue.ts |
Adds automatic format selection. |
packages/cli/src/terminal-text.ts |
Centralizes terminal-safe text utilities. |
packages/cli/test/accept.test.ts |
Tests acceptance end to end. |
packages/cli/test/mistake-recovery.test.ts |
Updates format validation expectations. |
packages/cli/test/queue-terminal.test.ts |
Tests terminal rendering and compatibility. |
packages/adapters/agent-plugin/test/wiring.test.ts |
Prevents agent-driven acceptance. |
| `${style.status('accepted')} ADR-${result.id}: ${result.title}`, | ||
| ` ${style.label('ratified by')} ${result.ratifiedBy} ${style.note(`at ${result.decidedAt}`)}`, | ||
| ` ${style.path(result.path)}`, |
There was a problem hiding this comment.
Fixed in 9eeecdd. The success output and the refusal messages (which quote corpus ids, paths, and finding text) now go through cleanText, the same helper the queue view uses. A new test writes a title containing ESC[2J, an OSC title sequence, and BEL, then asserts none of them reach stdout. It failed before the fix with the raw sequences in stdout. JSON output was already escaped and is unchanged.
| const blockers = acceptBlockers(item); | ||
| if (blockers.length > 0) { | ||
| for (const line of wrapText(`blocked: ${blockers.join('; ')}`, available)) lines.push(`${indent}${style.yellow(line)}`); | ||
| } else { | ||
| const command = `adr accept ${id} --by <identity>`; |
There was a problem hiding this comment.
Fixed in 9eeecdd, with a different mechanism than the one suggested. I didn't carry more accepted-state data into QueueItem, because QueueReport v1 is a public JSON contract that badges and the queue Action read. Instead, queue.ts dry-runs the same pure acceptAdrSource transition for each item, in the terminal view only, and shows the hint only when it would succeed. Otherwise it shows that refusal. This covers the empty-deciders case and any other refusal (layout, a future invariant) without a second rule set to keep in sync. The comprehensive fixture's 0010 now renders blocked: ADR-0010 would not be a valid accepted record: an accepted decision must name at least one decider, unless it was imported. The new assertion failed before the fix. Markdown and JSON output are unchanged.
ADR-0044 (proposed) records both changes. It is needed because `accept` grows the public write surface from two commands to three, which is a semver commitment under ADR-0031. adr accept <id> --by <identity> ratifies a proposed record. It sets status, provenance.ratifiedBy, and review.decidedAt (RFC 3339 with seconds, per ADR-0043) and changes no other line. A yaml round trip reformats 39 of this corpus's 44 records, so acceptAdrSource in @adrkit/core splices lines instead. It then re-parses the result and refuses unless every other field is semantically unchanged. That re-check caught a real bug during development: when provenance was absent and review was the last block, decidedAt landed under the new provenance block. The transition is pure; the CLI reads the clock. It refuses, with exit 1 and the file untouched, a record that is not proposed, has an unresolved objection, is short of its review.quorum, has lint errors, or would not be a valid accepted record. --by is required, is never inferred, and never counts as an approval. The agent plugin's wiring test now fails if any command, skill, or agent mentions adr accept. That test was observed failing against an injected mention (ADR-0016). adr queue --format now defaults to auto, following ADR-0033: a TTY gets a width-aware list with SLA state, days to deadline, approvals against quorum, objections, and either the accept command or the reason it is blocked. Piped output is the Markdown report byte for byte. The existing queue tests fail when auto is mutated to always choose terminal. Display-width helpers move from graph.ts to terminal-text.ts so both views share one implementation. The committed Action bundles are byte-identical when rebuilt with the pinned Bun 1.3.14. Signed-off-by: Mark Beacom <m@beacom.dev>
…cord as invalid ADR-0044 gains action item 7: flip its four public proposed-state qualifiers when it is accepted, the lesson ADR-0040's item 6 records. Its trade-offs now say why a durable commitment is still two-way-door before 1.0.0. adr accept now finds a record whose YAML does not parse by its file name. Such a record's findings carry a path and no id, so it used to exit 2 as an unknown id instead of 1 as existing but invalid. The new test fails without the file-name match. Signed-off-by: Mark Beacom <m@beacom.dev>
The edit that named accept as the third writing command left one overlong line in AGENTS.md; this rewraps it to the file's width. Signed-off-by: Mark Beacom <m@beacom.dev>
e58c0f3 to
ffa448a
Compare
…succeeds Two review findings on #250, both reproduced by a test that failed first: - adr accept printed the record's title and path to the terminal unsanitized. A title with ANSI/OSC sequences (YAML "\u001b" escapes) reached stdout intact. The success output and the refusal messages, which quote corpus ids, paths, and finding text, now pass through the same cleanText helper the queue view uses. - The queue's terminal view offered `adr accept 0010` for a proposed record with no deciders. That record is valid as proposed but not as accepted, so the command would refuse. Review state alone cannot see this. queue.ts now dry-runs the same pure acceptAdrSource transition for each item (terminal view only) and shows its refusal instead of the hint. A record that cannot be re-read gets no hint. QueueReport v1 is unchanged; Markdown and JSON output are untouched. ADR-0044 and AGENTS.md now say the hint comes from that dry run. Signed-off-by: Mark Beacom <m@beacom.dev>
* chore(release): prepare v0.16.0 Moves the lockstep surface to 0.16.0: the four public packages, CLI_VERSION, SERVER_INFO and server.json, bun.lock's workspace entries, and every documented pin (README, ci.mdx, badges.mdx, quickstart, the site hero, RELEASING.md, the bug-report template). The CHANGELOG's Unreleased section becomes 0.16.0, with what it was missing: - Security: the Action bundles' undici 6.29.0 (GHSA-rfgv-xxqx-mfg5, #251). It is the main reason to release now, since published v0 consumers still run 6.27.0. - Added: acceptAdrSource, a new @adrkit/core runtime export, which the release policy requires calling out. - Changed: adr queue --format defaults to auto (only a terminal sees a difference), and the Bun 1.4.2 toolchain move, which shrinks the Action bundles by dropping unused zod locales and helpers. The docs also catch up with adr accept, which adrkit.dev has described since #250 merged but npm doesn't ship yet: the AGENTS.md status line and the CLI README command lists name it, the CLI README and root README gain a short queue-and-accept section with the terminal view, and the container section lists accept among the commands that write. The Action bundles rebuild byte-identical under linux/amd64 Bun 1.4.2, and release:pack prepares all five packages for v0.16.0. Signed-off-by: Mark Beacom <m@beacom.dev> * docs(readme): name adr accept in the project status table The queue row now says the shipped workflow includes ratifying from the queue, not only reporting it. Signed-off-by: Mark Beacom <m@beacom.dev> * docs(changelog): don't claim the bundles' other dependencies are unchanged The 0.16.0 Changed entry said every other bundled dependency was unchanged, which contradicts the undici update under Security in the same release. It now says that, apart from zod's dropped locales and helpers and that undici update, the bundles contain the same modules as 0.15.0. I checked that against both bundles at v0.15.0 and on main. Signed-off-by: Mark Beacom <m@beacom.dev> --------- Signed-off-by: Mark Beacom <m@beacom.dev>


What and why
adr queueshows what is waiting for a decision but gives no way to make one. Accepting a record today is a hand edit, and the hand edit reliably dropsreview.decidedAt: neither ADR-0043 (#247) nor ADR-0040 (#248) recorded when it was ratified. And in a terminal the queue's Markdown tables print as raw pipes and wrap unreadably.ADR-0044 (proposed) records both changes. A record is needed because
acceptgrows the public write surface from two commands to three, and the CLI is a semver commitment under ADR-0031.adr accept <id> --by <identity>status,provenance.ratifiedBy, andreview.decidedAt(RFC 3339 with seconds, per ADR-0043). Ayamlround trip reformats 39 of this corpus's 44 records, soacceptAdrSource(in@adrkit/core, pure) edits those lines in place. It then re-parses the file and refuses unless every other field is semantically unchanged.decidedAtlanding under a newly addedprovenance:block.1, and leaves the file untouched when the record:proposed;review.quorum;--byis required and never inferred, and it never counts as an approval.adr accept. That is a test-only change; no plugin content changes.adr queueterminal view--formatdefaults toauto, following ADR-0033. A TTY gets a list that fits the window: SLA state, days to deadline, tier, approvals against quorum, objections, route, item findings, and eithernext: adr accept <id> --by <identity>or the reason acceptance is blocked./adr-queue, and SC-001.graph.tstoterminal-text.ts, sographandqueueshare one implementation.Checklist
proposed, with the argument (Options A–D, trade-offs, falsifiers).@adrkit/corechanged, but the rebuiltpackages/ci/distunder pinned Bun 1.3.14 is byte-identical. The Actions never import the new export. I will re-check this under linux/amd64 1.4.2 after build: move the pinned Bun toolchain from 1.3.14 to 1.4.2 #249.adr acceptmention;automutated to always chooseterminal(5 failures);bun run typecheck && bun run build && bun test && bun run lintpass (3,192 tests). The built CLI was smoke-tested under Node.Notes for reviewers
MANIFEST.mdand the sameAGENTS.mdsection, so I will rebase and re-runemit:manifestafter docs(adr): accept ADR-0040 #248 lands. build: move the pinned Bun toolchain from 1.3.14 to 1.4.2 #249 (the Bun bump) is independent, but after it lands I'll rebase once more so the dist gate is checked under 1.4.2.adr accepthas only run against scratch copies of fixture corpora, never the real one. Its first real use is ADR-0044's action item 6, run by the maintainer after merge. Item 7 lists the four(**proposed**)qualifiers to flip in that same PR, because the command only changesstatus.ratifiedByis now written on human-authored records too. The schema always allowed it. The trade-off is recorded in the ADR.provenance: {…}/review: {…}blocks get a message saying to edit by hand, rather than being reformatted.