# Architecture Decision Records

This directory holds rio's architecture decision records (ADRs), in the style
described at [adr.github.io](https://adr.github.io/) and in Michael Nygard's
original template. Each record states one decision: the situation that called
for it, what was decided, and what follows from it.

## Names

Decisions are recorded here and nowhere else ([ADR-0144](0144-decisions-live-in-adr-only.md)).

- **D1 to D136** are the names the old decision log in AGENTS.md used. Source comments
  and the changelog still cite them. A record's **Decision:** line carries its D number.
  - D1 to D111 are ADR-0001 to ADR-0111.
  - D112 to D136 are ADR-0118 to ADR-0143: `grep -l 'Decision:\*\* D123' adr/*`.
  - ADR-0112 to ADR-0117 never had one.
- **From ADR-0144 on** a decision has one name: `ADR-NNNN`.

The old log, with its implementation detail: `git show 1400edf:AGENTS.md`.

## Writing a new record

1. Copy [template.md](template.md) to `NNNN-short-title.md`, next free number.
2. Add a row to the index below: the record's own title, status and date.
3. Never rewrite an accepted record. Write a new one, and change only the old one's
   **Status** line ("Superseded by ADR-NNNN", "Amended by ADR-NNNN") and its index row.
4. Give it a CHANGELOG.md entry citing `ADR-NNNN`, or an exemption with a reason in
   `rio-core/tests/changelog.test`.

## Checking the set

```sh
tclsh adr/check.tcl
```

It must print `ALL PASS`. It holds the index against the records both ways (title,
status, date) and the D numbers gap-free. Files only: no Tk, no display, no network.

## Statuses

| Status | Meaning |
| ------ | ------- |
| Accepted | In force. |
| Accepted, not implemented | In force as design; no code yet. |
| Amended by ADR-NNNN | In force, with a later record changing part of it. |
| Superseded by ADR-NNNN | Replaced; kept for its reasoning. |
| Rejected | Tried or considered and reversed; kept so it is not proposed again. |

## Index

| ADR | Title | Status | Date |
| --- | ----- | ------ | ---- |
| [0001](0001-three-layers-core-frontends-server.md) | Three layers: core, frontends, optional server | Accepted; amended by 0030 | 2026-06-24 |
| [0002](0002-protocol-first-core-api.md) | The core API is a transport-independent message protocol | Accepted; amended by 0030 | 2026-06-24 |
| [0003](0003-core-owns-the-document.md) | The core owns the document; frontends are views | Accepted | 2026-06-24 |
| [0004](0004-tcl-tk-and-ck.md) | Tcl/Tk for core and GUI, Ck for the TUI | Accepted; the TUI is deferred by 0112 | 2026-06-24 |
| [0005](0005-keyboard-only-tui.md) | The TUI is keyboard-only | Accepted, not implemented (the TUI is deferred, 0112) | 2026-06-24 |
| [0006](0006-windows-tui-via-cygwin.md) | Windows TUI through Cygwin, PDCurses as fallback | Accepted, not implemented (the TUI is deferred, 0112) | 2026-06-24 |
| [0007](0007-git-by-shelling-out.md) | Git by shelling out to `git` | Accepted | 2026-06-24 |
| [0008](0008-llm-provider-interface.md) | LLM access behind a stable provider interface | Accepted | 2026-06-24 |
| [0009](0009-layout-policy-as-shared-function.md) | The responsive layout rule is a shared pure function | Accepted, not implemented (the TUI is deferred, 0112) | 2026-06-24 |
| [0010](0010-async-via-event-loop.md) | Asynchrony through the event loop and coroutines | Accepted | 2026-06-24 |
| [0011](0011-jsonl-wire-protocol.md) | Newline-delimited JSON wire protocol | Accepted; amended by 0113 | 2026-06-24 |
| [0012](0012-document-model-lines.md) | Document model: a list of lines, `line.col` positions | Accepted | 2026-06-24 |
| [0013](0013-layout-regions.md) | Fixed layout regions and a collapsible section stack | Accepted; amended by 0035 | 2026-06-24 |
| [0014](0014-responsive-tiers.md) | Responsive tiers and the unified-diff fallback | Accepted, not implemented (the TUI is deferred, 0112) | 2026-06-24 |
| [0015](0015-no-terminal-pane.md) | No terminal pane; a headless command primitive only | Accepted | 2026-06-25 |
| [0016](0016-plugins-as-protocol-participants.md) | A plugin is a protocol participant | Accepted, not implemented (the general plugin platform is deferred) | 2026-06-24 |
| [0017](0017-contribution-points.md) | Contribution points for extensions | Accepted, not implemented (the general plugin platform is deferred) | 2026-06-24 |
| [0018](0018-declarative-ui-contributions.md) | UI contributions are declarative | Accepted, not implemented (the general plugin platform is deferred) | 2026-06-24 |
| [0019](0019-plugin-manifest-and-permissions.md) | Plugin manifest and permissions; no marketplace platform | Accepted; distribution amended by 0039 | 2026-06-24 |
| [0020](0020-agent-orchestration-in-core.md) | Agent orchestration in the core, providers and tools outside | Accepted | 2026-06-24 |
| [0021](0021-plain-text-config-xdg.md) | Plain-text configuration in XDG locations, never executed | Accepted; session storage amended by 0031 | 2026-06-25 |
| [0022](0022-encoding-line-endings-cursor.md) | Preserve encoding and line endings; cursors are frontend-local | Accepted | 2026-06-25 |
| [0023](0023-keybindings-as-data.md) | Keybindings are data | Accepted; amended by 0143 | 2026-06-25 |
| [0024](0024-themes-as-role-data.md) | Themes are semantic-role data files | Accepted; amended by 0142 | 2026-06-25 |
| [0025](0025-shape-aware-json-encoding.md) | JSON encoding is shape-aware, never value-sniffed | Accepted | 2026-06-25 |
| [0026](0026-agent-first-slice-api-key.md) | The agent's first slice, on the official API key only | Accepted; amended by 0034, 0069, 0083, 0104 | 2026-06-27 |
| [0027](0027-monochrome-unicode-icons.md) | Icons are monochrome Unicode glyphs | Accepted | 2026-06-29 |
| [0028](0028-compare-view.md) | A core line diff and a read-only compare view | Accepted | 2026-06-29 |
| [0029](0029-gui-as-socket-client.md) | The GUI can drive a remote core over a socket | Superseded by 0030 | 2026-06-30 |
| [0030](0030-always-a-channel-client.md) | The frontend is always a client over a channel | Accepted; amended by 0096 | 2026-06-30 |
| [0031](0031-sessions-and-preferences.md) | Sessions and preferences, split by owner, out of tree | Accepted | 2026-07-01 |
| [0032](0032-syntax-highlighting-in-frontend.md) | Syntax highlighting: swappable per-line scanners in the frontend | Accepted; the viewport scoping deferred here arrived with 0133 | 2026-07-01 |
| [0033](0033-two-editor-groups.md) | Two side-by-side editor groups | Accepted | 2026-07-06 |
| [0034](0034-agent-system-prompt.md) | A core-owned, provider-agnostic system prompt | Accepted; amended by 0070, 0079, 0105 | 2026-07-08 |
| [0035](0035-tool-windows-dock-sites.md) | Tool windows live in dock sites, not in the document tabs | Accepted | 2026-07-12 |
| [0036](0036-find-replace-engine-in-core.md) | Find and replace: the engine in the core, a bar in the GUI | Accepted | 2026-07-12 |
| [0037](0037-stale-link-watchdog.md) | Stale-link detection at the protocol layer | Accepted | 2026-07-12 |
| [0038](0038-editing-modes.md) | Editing modes as a bind-tag layer | Accepted; amended by 0041, 0143 | 2026-07-12 |
| [0039](0039-extension-repositories.md) | Extension repositories over plain HTTP, apt-sources style | Accepted; amended by 0066, 0107, 0109, 0124 | 2026-07-17 |
| [0040](0040-column-editing.md) | Column editing as a GUI-only vertical cursor | Accepted | 2026-07-23 |
| [0041](0041-unbundled-editing-modes.md) | The core ships one editing mode; emacs and vi are extensions | Accepted | 2026-08-03 |
| [0042](0042-files-pane-rich-list.md) | The files pane is a rich list drawn on a text widget | Accepted; amended by 0043, 0087 | 2026-08-06 |
| [0043](0043-shared-rich-list-and-git-flags.md) | A shared rich-list component; git flags in the files pane | Accepted | 2026-08-06 |
| [0044](0044-pane-context-menus-git-writes.md) | Pane context menus and the first git write operations | Accepted | 2026-08-07 |
| [0045](0045-git-commit-bar.md) | Commit from a bar that appears only when something is staged | Accepted; amended by 0081 | 2026-08-07 |
| [0046](0046-highlighters-by-filename.md) | Highlighters can register by file name | Accepted | 2026-08-10 |
| [0047](0047-fs-changed-event.md) | Disk writes are announced with `fs.changed` | Accepted | 2026-08-18 |
| [0048](0048-file-management-actions.md) | File management as core operations | Accepted; target directory rule amended by 0087 | 2026-08-18 |
| [0049](0049-line-number-gutter.md) | A line-number gutter drawn from the text widget's geometry | Accepted | 2026-08-19 |
| [0050](0050-cursor-position-status.md) | Cursor position in the status bar | Accepted | 2026-08-19 |
| [0051](0051-find-in-files.md) | Find in files: a core engine and a bottom results panel | Accepted; scope amended by 0052 | 2026-08-19 |
| [0052](0052-search-panel.md) | One search panel: two engines, three scopes | Accepted | 2026-08-19 |
| [0053](0053-assisted-not-autonomous.md) | LLM integration is assisted, not autonomous | Accepted; amended by 0084 | 2026-08-23 |
| [0054](0054-utf-8-source-encoding.md) | Every entry point pins the source encoding to UTF-8 | Accepted | 2026-09-03 |
| [0055](0055-core-answers-host-questions.md) | Questions about the core's host are answered by the core | Accepted | 2026-09-03 |
| [0056](0056-editor-font-override.md) | The editor font is a user override on the theme font | Accepted; amended by 0143 | 2026-09-03 |
| [0057](0057-tab-overflow.md) | Tab overflow: page, wrap, or list | Accepted; amended by 0074, 0078 | 2026-09-03 |
| [0058](0058-preferences-window.md) | A Preferences window that owns no state | Accepted; amended by 0085, 0092 | 2026-09-03 |
| [0059](0059-menu-hover-patch-reverted.md) | Patching Tk's menu click behaviour | Rejected (shipped, then reverted) | 2026-09-04 |
| [0060](0060-current-line-highlight.md) | Current-line highlight | Accepted | 2026-09-04 |
| [0061](0061-gutter-line-selection.md) | Click a line number to select the line | Accepted | 2026-09-04 |
| [0062](0062-hide-dotfiles.md) | Hide dotfiles in the files pane by default | Accepted | 2026-09-04 |
| [0063](0063-tooltips.md) | Hover tooltips for glyph controls | Accepted | 2026-09-04 |
| [0064](0064-bounded-view-menu.md) | Menus are kept within screen height by grouping | Accepted | 2026-09-04 |
| [0065](0065-second-provider-contract.md) | A second provider, and hardening the provider contract | Accepted; distribution amended by 0066; keys made per profile by 0138 | 2026-09-04 |
| [0066](0066-installable-providers.md) | Providers install from repositories, behind a versioned API | Accepted; amended by 0069 | 2026-09-04 |
| [0067](0067-extensions-in-settings.md) | The Extensions window moves to the Settings menu | Accepted; the item moved once more in 0137 | 2026-09-04 |
| [0068](0068-help-text-vs-controls.md) | Static help must look different from controls | Accepted | 2026-09-04 |
| [0069](0069-claude-as-extension.md) | Claude is an installable provider; echo is the only built-in | Accepted | 2026-09-04 |
| [0070](0070-user-and-project-prompts.md) | A defined place for user and project prompts | Accepted; amended by 0079, 0105 | 2026-09-04 |
| [0071](0071-relative-line-numbers.md) | Relative line numbers | Accepted | 2026-09-05 |
| [0072](0072-anonymous-workspace.md) | A session for work with no project open | Accepted | 2026-09-08 |
| [0073](0073-compare-menu.md) | Compare gets its own top-level menu | Accepted | 2026-09-08 |
| [0074](0074-buffer-picker.md) | A bounded buffer picker replaces the Tabs menu | Accepted; generalised by 0092 | 2026-09-08 |
| [0075](0075-find-menu.md) | A top-level Find menu | Accepted | 2026-09-08 |
| [0076](0076-about-build-identity.md) | About rio shows the build's commit identity | Accepted; the release version it lacked arrived with 0130 | 2026-09-08 |
| [0077](0077-open-multiple-files.md) | Open several files from the native chooser | Accepted | 2026-09-08 |
| [0078](0078-multi-line-tab-rows.md) | Multi-line tabs flow into packed, justified rows | Accepted | 2026-09-08 |
| [0079](0079-per-provider-prompts.md) | A per-provider prompt layer | Accepted | 2026-09-08 |
| [0080](0080-git-discard.md) | Git discard for everyday use | Accepted; amended by 0093, 0097 | 2026-09-08 |
| [0081](0081-commit-message-body.md) | An optional commit message body | Accepted | 2026-09-08 |
| [0082](0082-agent-busy-indicator.md) | A busy indicator while the agent works | Accepted | 2026-09-09 |
| [0083](0083-agent-run-command.md) | The agent runs commands: gated, asynchronous, time-bounded | Accepted; amended by 0084 | 2026-09-09 |
| [0084](0084-command-allow-list.md) | Human-authored command allow-lists | Accepted | 2026-09-09 |
| [0085](0085-agent-config-in-preferences.md) | Agent configuration lives in Preferences | Accepted | 2026-09-09 |
| [0086](0086-os-file-drop.md) | Drop a file on the window to open it, with optional tkdnd | Accepted | 2026-09-09 |
| [0087](0087-files-tree.md) | The files pane becomes a tree from the project root | Accepted | 2026-09-09 |
| [0088](0088-reopen-last-folder.md) | Reopen the last folder on launch | Accepted; amended by 0089 | 2026-09-09 |
| [0089](0089-remember-tree-shape.md) | Remember the unfolded tree; survive a deleted folder | Accepted | 2026-09-09 |
| [0090](0090-undo-coalescing.md) | Undo coalescing by word, decided in the core | Accepted | 2026-09-10 |
| [0091](0091-in-tree-user-manual.md) | The user manual lives in the source tree | Accepted | 2026-09-10 |
| [0092](0092-theme-picker.md) | The theme menu becomes a bounded picker | Accepted | 2026-09-10 |
| [0093](0093-discard-tree-and-bulk.md) | Discard from the file tree, and discard all | Accepted | 2026-09-10 |
| [0094](0094-stale-buffers.md) | Buffers detect changes to their files | Accepted | 2026-09-10 |
| [0095](0095-headless-never-asks.md) | A headless run never asks a human anything | Accepted | 2026-09-11 |
| [0096](0096-rio-does-not-dial.md) | rio speaks the protocol; it does not dial | Accepted | 2026-09-11 |
| [0097](0097-rename-aware-discard.md) | Discarding a rename restores the old name | Accepted | 2026-09-11 |
| [0098](0098-untracked-folder-files.md) | Files inside an untracked folder can be tracked individually | Accepted | 2026-09-11 |
| [0099](0099-help-viewer.md) | rio shows its own manual | Accepted; amended by 0100 | 2026-09-11 |
| [0100](0100-manual-renderer.md) | The manual is rendered, with working links and search | Accepted | 2026-09-11 |
| [0101](0101-plan-mode.md) | Plan mode: a core tool and a readable plan view | Accepted; amended by 0102, 0103 | 2026-09-11 |
| [0102](0102-plan-approval-policy.md) | The edit policy is chosen when a plan is approved | Accepted | 2026-09-11 |
| [0103](0103-plan-tool-in-every-mode.md) | The plan tool is offered in every mode | Accepted | 2026-09-11 |
| [0104](0104-stop-instead-of-step-cap.md) | No step cap; a Stop button instead | Accepted | 2026-09-11 |
| [0105](0105-shipped-prompt-visible.md) | rio's shipped prompt is complete and visible | Accepted | 2026-09-11 |
| [0106](0106-model-and-effort-options.md) | Model and effort are provider-declared options | Accepted; extended by 0135 and 0138 | 2026-09-12 |
| [0107](0107-semver-extension-updates.md) | Extension versions are semver and are compared | Accepted | 2026-09-12 |
| [0108](0108-editor-context-menu.md) | The editor has a context menu built from the Edit menu's table | Accepted; amended by 0121, 0143 | 2026-09-12 |
| [0109](0109-https-repositories.md) | https beside http, trusted from the host's CA store | Accepted; amended by 0110, 0111, 0120 | 2026-09-12 |
| [0110](0110-agent-https-hostname-checks.md) | The agent refuses https without host-name checks unless allowed | Accepted; amended by 0120 | 2026-09-15 |
| [0111](0111-certificate-exceptions.md) | A certificate that fails verification can be accepted by fingerprint | Accepted; amended by 0120 | 2026-09-15 |
| [0112](0112-tui-deferred.md) | The TUI is deferred after a successful Ck spike | Accepted | 2026-06-27 |
| [0113](0113-error-code-taxonomy.md) | A small, stable error-code taxonomy | Accepted | 2026-06-26 |
| [0114](0114-single-root-project.md) | A project is one root folder held by the core | Accepted | 2026-06-27 |
| [0115](0115-dependency-policy.md) | Hard dependencies are minimal; optional ones must degrade cleanly | Accepted | 2026-09-09 |
| [0116](0116-verification-policy.md) | Verification is automated, offline, and proven against injected faults | Accepted | 2026-09-11 |
| [0117](0117-derived-facts-register.md) | A fact kept in two places must have a guard | Accepted | 2026-09-10 |
| [0118](0118-language-picked-by-hand.md) | A buffer's language can be picked by hand | Accepted | 2026-09-16 |
| [0119](0119-change-with-agent.md) | Change with Agent: a request about the selected text, and only that | Accepted | 2026-09-16 |
| [0120](0120-core-wide-unchecked-https-switch.md) | One core-wide switch for https without host-name checks | Accepted | 2026-09-17 |
| [0121](0121-context-menu-outside-the-editor.md) | A context menu on every text widget outside the editor | Accepted; amended by 0143 | 2026-09-17 |
| [0122](0122-missing-dependency-names-its-package.md) | A missing dependency names the package to install | Accepted | 2026-09-17 |
| [0123](0123-window-and-taskbar-icon.md) | rio has a window and taskbar icon | Accepted; the artworks it compares were removed by 0127 | 2026-09-18 |
| [0124](0124-signed-repositories.md) | A repository can be signed, and rio checks it | Accepted; amended by 0125; trust on first use superseded by 0126 | 2026-09-16 |
| [0125](0125-repository-signing-keys-list.md) | Every repository signing key rio trusts is listed, and can be forgotten | Accepted; amended by 0126 | 2026-09-19 |
| [0126](0126-signing-key-confirmed-by-the-user.md) | A signing key is trusted when the user confirms it, never because it arrived first | Accepted | 2026-09-19 |
| [0127](0127-artwork-rio-can-pass-on.md) | rio ships only artwork it can pass on | Accepted | 2026-09-20 |
| [0128](0128-mit-license.md) | rio is MIT-licensed | Accepted; amended by 0129 | 2026-09-20 |
| [0129](0129-extension-payload-carries-its-licence.md) | An extension payload carries its licence inside it | Accepted | 2026-09-20 |
| [0130](0130-release-version-and-contract-versions.md) | rio is 0.1.0: semver for releases, integers for contracts | Accepted | 2026-09-20 |
| [0131](0131-changelog-with-a-guard.md) | rio has a changelog, and a guard keeps it current | Accepted; amended by 0144 | 2026-09-20 |
| [0132](0132-decline-a-file-too-big-to-open.md) | A file too big or too binary to open is declined, not read | Accepted; the budget was raised by 0133 | 2026-09-20 |
| [0133](0133-big-files-are-slow-for-what-rio-does-to-them.md) | A big file is slow because of what rio does to it, not because it is big | Accepted | 2026-09-20 |
| [0134](0134-headless-run-cannot-receive-real-input.md) | A headless run must not be able to receive real input | Accepted; amended by 0140 | 2026-09-21 |
| [0135](0135-provider-declares-its-own-settings.md) | A provider declares its own settings, and rio renders them | Accepted; where the settings window lives reversed by 0137 — the option vocabulary, the `thinking` verb and `provider-api 4` stand; amended by 0138 | 2026-09-22 |
| [0136](0136-install-script-per-platform.md) | One install script per platform, and a launcher that makes rio an application | Accepted; amended by 0141 | 2026-09-22 |
| [0137](0137-extensions-configure-themselves.md) | A top-level Extensions menu; each extension configures itself | Accepted | 2026-09-23 |
| [0138](0138-provider-profiles-and-file-options.md) | A provider keeps several named configurations, and an option may name a file | Accepted | 2026-09-23 |
| [0139](0139-autosave-recovery-copies.md) | Autosave writes a separate recovery copy, never the file you are editing | Accepted | 2026-09-26 |
| [0140](0140-macos-platform-facts-from-tk.md) | On macOS, platform facts are asked of Tk, not assumed | Accepted; amended by 0141 | 2026-09-29 |
| [0141](0141-macos-installer-and-rio-app.md) | macOS gets its own installer, and rio.app is a copy of wish | Accepted | 2026-09-29 |
| [0142](0142-aqua-theme-appearance-and-font-floor.md) | On Aqua, the theme reaches the native controls, and chrome text has a floor | Accepted | 2026-09-29 |
| [0143](0143-mac-keys-and-right-button.md) | On a Mac, rio uses the Mac's keys and its right mouse button | Accepted | 2026-09-29 |
| [0144](0144-decisions-live-in-adr-only.md) | Decisions live in adr/ only; AGENTS.md is instructions | Accepted | 2026-10-02 |
| [0145](0145-gui-one-file-per-concern.md) | The GUI is one file per concern; rio-gui.tcl is the entry | Accepted | 2026-10-02 |
| [0146](0146-undo-has-a-budget.md) | Undo has a budget, and a step holds a text once | Accepted | 2026-10-02 |
