Skip to content

Architecture decision records

One record per decision that shapes the code (ADR 0001), in a trimmed MADR 4 format: 0000-template.md.

ADR Title Status Date
0001 Record architecture decisions accepted 2026-10-23
0002 A modulith of a kernel, modules and lets accepted 2026-10-23
0003 Heads are views on head-agnostic operations accepted 2026-10-23
0004 Discovery through packages, entry points and pluggy accepted 2026-10-23
0005 Dependency injection by type through ServiceLets accepted 2026-10-23
0006 Plugins are installed through the uv tool receipt accepted 2026-10-23
0007 Plugins are scaffolded with copier from this repository accepted 2026-10-23
0008 Releases are derived from conventional commits accepted 2026-10-23
0009 Unit, integration and e2e tests, as in the Aignostics Python SDK accepted 2026-10-23
0010 Towards a reactive workflow engine proposed 2026-10-23
0011 Agentic coding standards are enforced by the repository accepted 2026-10-23
0012 Layered configuration with per-module settings accepted 2026-10-23
0013 Pull-request-based development and releases accepted 2026-10-23
0014 Agent skills ship with Heinzelmännchen and as a Claude Code plugin accepted 2026-10-23
0015 Health checks are lets, aggregated by hz system doctor accepted 2026-10-23
0016 The project is called Heinzelmännchen; the command is hz accepted 2026-10-23
0017 First-party plugins live in the core repository as workspace packages accepted 2026-10-23
0018 One administrative namespace: hz system; skills live in agent-skills/ accepted 2026-10-23
0019 MCP and REST heads derive everything from operations accepted 2026-10-23
0020 Add a daemon head for background and reactive operation proposed 2026-10-23
0021 Workflows are Markdown contracts with YAML frontmatter proposed 2026-10-23
0022 Workflows embedded in documents are discovered by connectors and run only once registered proposed 2026-10-23
0023 The inventory is compiled from declared, discovered and plugin-contributed entries and published as graphs proposed 2026-10-23
0024 People and teams come from a directory of record, are referenced by role, and are minimised proposed 2026-10-23
0025 Plugin distributions are named heinzel-<name> accepted 2026-10-23
0026 The plugin API is versioned with SemVer, and discovery checks each plugin's compatible core range proposed 2026-10-23
0027 Nothing is sent home: no telemetry, crash reports or usage pings accepted 2026-10-23
0028 Integrations are capability adapters; workflows name capabilities, never vendors proposed 2026-10-23
0029 Agent runners are capability adapters; core ships Claude Code and a generic command adapter proposed 2026-10-23
0030 Credentials live in the OS keychain behind a credentials service accepted 2026-10-24
0031 The CLI checks for a newer release hourly, anonymously and in the background, and says so on stderr accepted 2026-10-24
0032 Modules declare interactive setup steps; hz setup runs them all proposed 2026-10-24
0033 Read GitHub through its REST API with urllib; merged pull requests from the pulls list superseded 2026-10-24
0034 Read Jira and Confluence Cloud through their REST APIs with urllib, Basic auth and epoch-millisecond JQL superseded 2026-10-24
0035 Integrations go through the vendor's CLI as an ordinary employee accepted 2026-10-24
0036 make install installs every tool the build needs, pinned and checksummed, on macOS and Linux proposed 2026-10-24
0037 Plugins bring playbooks: asset types with discovery hints, template contracts and recommended settings proposed 2026-10-24
0038 Drafts are approved in place proposed 2026-10-24
0039 A web head: a React app served by the API head proposed 2026-10-24
0040 Layers and playbooks are plugins: a plugin may be a layer with a parent and a site, and layers stack general to specific proposed 2026-10-24
0041 Expert mode: newcomers see the everyday commands, hz system expert on shows all proposed 2026-10-24
0042 Read Google Workspace through the Google Workspace CLI (gws) accepted 2026-10-24
0043 Desktop notifications: one policy in the core, shown once, by hz's own macOS app proposed 2026-10-24
0044 Themes are data a plugin ships: module.theme(...) offers them, hz system theme use picks one proposed 2026-10-24
0045 A chat that knows where you are and acts through hz, with every change confirmed in code proposed 2026-10-24
0048 A terminal in the web head proposed 2026-10-24
0046 An installable web app on a fixed port, with paired devices and notifications from its service worker proposed 2026-10-24
0047 Plugins are managed from the web app, and adding one shows what it installs before it is confirmed proposed 2026-10-24
0049 Heinzelmännchen are durable agents: one session each, owned by hz proposed 2026-10-24
0050 Pydantic AI runs a turn; a coding agent is a handoff proposed 2026-10-24
0051 The workflow module becomes the heinzel module accepted 2026-10-24
0052 The graph emerges from recorded reads and writes proposed 2026-10-24
0053 The team graph is drawn with React Flow and laid out by elk; Mermaid is only an export proposed 2026-10-24
0054 Observability is whether each team promise is kept: telemetry in, a graph out proposed 2026-10-24
0055 Connectors show their sign-in, test live on demand, and bind themselves when they are alone accepted 2026-10-24
0056 Modules contribute status items, header actions and Today widgets as data, viewed from their own operations proposed 2026-10-24
0057 AI access is a module: one choice like the theme, options are adapters of ai-access proposed 2026-10-24
0058 Soundscapes are data a plugin ships, chosen like a theme: module.soundscape(...), hz system sound use proposed 2026-10-24
0059 The calendar and the mail write through hz, after a confirmation proposed 2026-10-24
0060 Themes follow the time of day and the weather: variants on a closed vocabulary of signals proposed 2026-10-24
0062 Heinzelmännchen find the MCP servers and skills you already gave Claude, read-only first proposed 2026-10-25
0061 Plugins ship skills every AI path can use: module.skill(...), read-only tools, an explicit export proposed 2026-10-24
0065 Plugins may ship versioned React views alongside their Python declarations proposed 2026-10-25
0067 hz owns sessions across Pydantic and native CLI turns proposed 2026-10-25
0068 Human approval is a scoped proof carriable by every authorized head proposed 2026-10-25
0072 Plugins declare navigation and Settings metadata as checked data proposed 2026-10-25
0082 Plugin documentation is generated from the kernel and recorded by the tooling every plugin shares proposed 2026-10-24

Amendments: 0025 amends 0016 (plugin naming); 0013 amends 0008; 0020 and 0021 amend 0010; 0032 amends 0018 (hz setup); 0028 amends 0024 (the directory is a capability); 0038 amends 0010 and 0020 (approval in place instead of an approval queue; hz inbox is a view); 0039 amends 0018 (hz ui) and 0019 (a token-protected web head may expose what the unauthenticated REST head doesn't); 0041 amends 0018 (hz system expert; help lists the everyday commands) and 0039 (the navigation lists the same ones); 0043 amends 0020 (every tick ends by popping up due notices) and 0039 (the notify operations are web endpoints, not REST ones); 0045 amends 0018 (hz chat), 0029 (hz operations as agent tools through the MCP head) and 0039 (chat sessions instead of the stateless resend); 0048 amends 0039 (a terminal over a WebSocket; inline styles allowed); 0046 amends 0039 (a fixed port and paired devices) and 0043 (notifications in the web app); 0047 amends 0006 (add shows first and installs on --confirm, from PyPI or https git only) and 0039 (plugin changes are web endpoints, not REST ones; POST /api/restart); 0049 amends 0010 (a turn of a continuous session instead of a session per render), 0020 (the tick wakes Heinzelmännchen by key; team state on hz/state with leases), 0021 (one contract per Heinzelmännchen, in versions; records become turn bodies), 0029 (the runner no longer owns the session), 0038 (lessons; hz drafts and hz approve) and 0045 (the sidebar chat with a Heinzelmännchen is its session), and in its durable-turns amendment 0028 (fingerprints per facet) and 0052 (downstream wakes only when the approved output moved); 0050 amends 0029 (Pydantic AI in the core as the turn engine; runners are the handoff) and 0045 (the chat on the same engine); 0051 amends 0021 (operation names), 0038 (hz inbox becomes hz drafts and hz approve), 0041 (the everyday set) and 0045 (heinzel.* settings in the never-run policy); 0052 amends 0021 (observed edges beside declared ones), 0023 (no entity schema or single-writer rule for now), 0038 (repeated nudges and the fallback, proposed) and 0049 (receipt-grade events are action entries; the report tool); 0054 amends 0037 (the core's graph module ships the graph keeper's playbook) and 0052 (telemetry logs in the team repository before the spine; hz report); 0055 amends 0028 (a sole installed adapter serves its capability automatically); 0057 amends 0029 (an AI access option names the agent runner that serves it) and 0050 ([heinzel] model gives way to [ai] routing); 0058 amends 0027, 0039 and 0046 (fixed allowlisted player origins while a stream is in effect; the player loads only after Play; amended by 0092); 0060 amends 0044 (variants that follow signals; a base theme is checked for contrast too); 0059 amends 0028 (a write mode and three reads for calendar and mail), 0038 (no place_draft for them: a mail draft is placed in the mailbox, a calendar write is confirmed) and 0042 (writes and their scopes). 0061 amends 0014 (plugin skills reach Claude Code through hz system skill export) and 0050 (skills are SkillLets; a turn gets an index and loads bodies on demand). ADRs 0021–0024 are proposed together and disagree on a few points (data file format, the shared inventory module, canonicalization, selector grammar); each lists them under More Information.

Supersessions: 0035 supersedes 0033 and 0034 (vendor CLIs instead of REST over urllib with tokens).

Rules

  • Statuses: proposed → accepted or rejected; later deprecated or superseded (with superseded-by).
  • Accepted records are immutable. Only their front matter changes (status, superseded-by, amended-by); a changed decision is a new ADR that supersedes or amends the old one, with the links set on both.
  • Numbers are sequential and never reused; files are NNNN-title-in-kebab-case.md.
  • This index lists every record with the title, status and date from its file; tests/unit/documentation_test.py checks the template, this table and the links between records.
  • New ADR: copy 0000-template.md or use the write-adr skill.

2026-10-23 retrofit

ADRs 0001–0020 were converted to this template on 2026-10-23. The conversion only restructured them: decisions, statuses and dates are unchanged. "Alternatives considered" became Considered Options, "Forces" became Decision Drivers, status annotations became amends/amended-by front matter, and each record gained a Confirmation that names the checks that exist in the repository. Where no alternatives were written down, the record says so instead of inventing them. Two factual corrections came with it: ADR 0004 names the pluggy hook heinzel_lets (renamed from joy_lets), and ADR 0017, already implemented with the clock plugin (plugins/heinzel-clock), was accepted.

| 0063 | World Clock is a local interactive globe in the right panel | proposed | 2026-10-25 | | 0064 | Themes can dress the core Heinzelmännchen with a palette and snow | proposed | 2026-10-25 | | 0071 | Text branding uses the name and original artwork | proposed | 2026-10-25 | | 0073 | Themes supply safe SVG layers for personal avatars | proposed | 2026-10-25 | | 0081 | State repositories: one writer per branch, append-only segments, the daemon pushes | proposed | 2026-10-25 | | 0080 | One invite line and a reviewed first win | proposed | 2026-10-25 | | 0084 | Private web sharing admits exact HTTPS origins and keeps device authentication | proposed | 2026-10-25 | | 0074 | Machine resource meters use bounded local sampling | proposed | 2026-10-25 | | 0083 | Restart and apply verifies the replacement server | proposed | 2026-10-25 | | 0079 | Core PyPI trusted publishing is isolated from builds | proposed | 2026-10-25 | | 0085 | Web updates verify the reloaded frontend | proposed | 2026-10-26 | | 0078 | Native desktop uses the shared lifecycle service and existing daemon | proposed | 2026-10-25 | | 0087 | Managed backend ownership is independent of the native window | proposed | 2026-10-27 | | 0088 | The collapsed sidebar keeps sound controls | proposed | 2026-10-27 | | 0089 | One selected workspace panel on every screen size | proposed | 2026-10-27 | | 0090 | Installed apps share their authenticated URL | proposed | 2026-10-28 | | 0091 | Confirmed plugin adds use server-owned operation receipts | proposed | 2026-10-28 | | 0092 | YouTube sign-in is an explicit player choice | proposed | 2026-10-28 | | 0093 | Touch users inspect updates before installing | proposed | 2026-10-28 | | 0094 | Plugins own their features and independent delivery | accepted | 2026-10-29 | | 0095 | The standard Heinzelmännchen theme owns the bundled soundscapes | accepted | 2026-10-29 | | 0096 | Release the plugin boundary at the first unused version | accepted | 2026-10-29 | | 0097 | Plugin authors use one small artifact-based toolchain | proposed | 2026-10-29 | | 0098 | Pages showcases the product and discovers public plugin metadata | proposed | 2026-10-30 | | 0099 | Review connected reads and local agent notifications separately | proposed | 2026-10-30 | | 0104 | Bind notification deduplication to observed source items | proposed | 2026-10-30 |