Architecture decisions¶
This is the decision log Evarness grew up with: one record per non-obvious or trade-off-heavy decision, with its context, the choice, the alternatives rejected and the consequences, as they were understood at the time.
Read it with three things in mind.
- A record is history, not a description of the current system. It is not edited when the code changes. The current system is described in Architecture and the pages under
docs/architecture/. When a record and the code disagree, the code is current and the record explains how it got there; a superseding record says so in its status line. - They predate the rename. Until 0.2.0 the project was IRIS, and the records say IRIS,
iris_harness,IRIS_*and~/.iris. Readevarness,EVARNESS_*and~/.evarness. - Some decisions concern code that is not in this repository. The finance, calendar, file-manager, planner and coding-agent domains live in a private tree. Their records are kept here because the harness decisions reference them; the Where column says
private tree.
Status pass, 2026-10-18. Every record cited by the code whose named files no longer existed, and every Proposed record, was checked against the code. Where something changed, a dated Status pass line sits under the record's own status and the Where column below says where the code lives now, or that it is superseded, private, implemented or not implemented. A record without that line was not re-checked and reads as written.
Records numbered 0001 to 0134 were written between May and October 2026. Thirteen of them (0078 to 0086, 0092, 0097, 0101 to 0103) were sections of the project decisions log rather than standalone files and were extracted on 2026-10-18; each says so under its title. Two records share the number 0123; both are kept. Numbers with no record were never written.
| # | Title | Status | Where | Cited by code |
|---|---|---|---|---|
| 0001 | Singular IRIS agent, skill packs only — no sub-agents | Accepted | ||
| 0002 | Privacy-aware Tier 3 — local vs cloud resolved by per-category sensitivity | Accepted | yes | |
| 0003 | OAuth tokens in the macOS Keychain via keyring; no custom vault |
Accepted | kernel/governance/vault/credentials.py |
yes |
| 0004 | Reuse the existing wiki/knowledge subsystem as the cross-domain correlation engi | Superseded | ||
| 0005 | Task vocabulary + soft migration of the legacy reminders module | Accepted | evarness/services/tasks/ |
yes |
| 0006 | Finance ingestion via PDF-from-email + manual drop; no Plaid/scraping in core | Accepted | yes | |
| 0007 | Multi-currency model — currency-required, group-by-currency aggregation, no impl | Accepted | yes | |
| 0008 | R2/R3 split for calendar writes — private events auto, attendee events prompt | Accepted | ||
| 0009 | No sandboxing for community skills — explicit trust model + governance enforceme | Accepted | yes | |
| 0010 | Entity extractor — tighten regex + hint validation, not rewrite | Accepted | memory/knowledge/entity_extractor.py |
yes |
| 0011 | Keychain credentials module — implementation shape | Accepted | ||
| 0012 | Manifest schema extension — implementation shape | Accepted | yes | |
| 0013 | Event bus promotion — implementation shape | Accepted | foundation/eventbus/ |
yes |
| 0014 | Tasks subsystem — implementation shape | Accepted | evarness/services/tasks/ |
yes |
| 0015 | PDF unlock helper — implementation shape | Accepted | private tree | |
| 0016 | Email accounts registry — implementation shape and DB location | Accepted | yes | |
| 0017 | Email classification — dynamic, hierarchical, self-learning categories | Accepted | yes | |
| 0018 | iris email bootstrap-categories CLI — implementation shape |
Accepted | evarness_personal/plugins/email_workflows/cli.py, discovery.py |
yes |
| 0019 | categories data model + iris email accept-categories CLI — implementation sh |
Accepted | evarness_personal/email/category_store.py |
yes |
| 0020 | Drift correction — user-driven corrections for systematic LLM naming errors | Accepted | yes | |
| 0021 | email-triage skill — implementation shape |
Superseded by ADR-0022 | yes | |
| 0022 | Resource-aware triage — pure-kNN per email, on-demand LLM batch review | Accepted | yes | |
| 0023 | kNN-gate measurement + holdout-labeling tools | Accepted | yes | |
| 0024 | Self-learning substrate — Phase 1 scope | Accepted | yes | |
| 0025 | email → wiki ingestion shape | Accepted | email_workflows/wiki_ingestion.py and evarness/memory/knowledge/event_subscribers.py |
yes |
| 0026 | email-search FTS shape | Accepted | yes | |
| 0027 | iris task + iris goal CLI — implementation shape |
Accepted | ||
| 0028 | email-followup skill — implementation shape |
Accepted | ||
| 0029 | Morning brief Phase 2 integration — implementation shape | Accepted | ||
| 0030 | Delivery-only Reminder primitive + channel routing — implementation shape | Accepted | ||
| 0031 | Finance data layer — schema, models, and hybrid institution registry | Accepted | ||
| 0032 | Pluggable secret store — keyring default, encrypted file vault optional | Accepted | yes | |
| 0033 | Statement extraction — Tier-3 LOCAL into a strict schema, injectable extractor | Accepted | ||
| 0034 | PDF extraction engine — pypdfium2 + pdfplumber, no AGPL; deterministic decoding | Accepted | ||
| 0035 | Deterministic numbers — don't let the LLM transcribe digits; read cells + reconc | Accepted | private tree | |
| 0036 | Per-document-type extractors — contract notes → trades, CAS → holdings | Accepted | private tree | |
| 0037 | iris finance CLI + per-currency net-worth semantics |
Accepted | private tree | |
| 0038 | Transaction categorization — hybrid rules + embedding kNN + corrections | Accepted | ||
| 0039 | Finance Monitor heartbeat + morning-brief integration | Accepted | private tree | yes |
| 0040 | Calendar agent foundation — provider abstraction, ICS first, one ICS implementat | Accepted | ||
| 0041 | Native Google Calendar provider — OAuth, provider mapping, syncToken delta | Accepted | private tree | |
| 0042 | Calendar write governance as a boundary module (not a governor-policy extension) | Accepted | private tree | |
| 0043 | Calendar C3 — meeting-prep routine, reconcile-based task/bill mirror, stdlib MCP | Accepted | private tree | yes |
| 0044 | Planner agent foundation — deterministic daily-plan synthesis | Accepted | private tree | |
| 0045 | Planner P1 — parallel wave execution + a registered planner agent type | Accepted | evarness/agent/agent_executor.py |
yes |
| 0046 | Planner P2 — grounded LLM plan narrative (brief headline dropped) | Accepted | ||
| 0047 | Document RAG foundation — index the user's docs (connector, not owner), cited re | Accepted | ||
| 0048 | Obsidian vault connector — front-matter, wikilinks, incremental sync | Accepted | ||
| 0049 | RAG grounded cited Q&A + tag scope + MCP exposure | Accepted | ||
| 0050 | RAG PDF + image ingestion with opt-in OCR | Accepted | ||
| 0051 | FileManager secure foundation — deny-by-default confinement, secret-aware reads, | Accepted | ||
| 0052 | FileManager governed mutations — R2/R3 + soft, reversible ops | Accepted | ||
| 0053 | FileManager cloud — Google Drive (read-only) + iCloud Drive as a local root | Accepted | ||
| 0054 | FileManager advanced security — redaction-on-share + sensitivity-tiered reads + | Accepted | ||
| 0055 | System agent — user-facing status + IRIS self-introspection | Accepted | ||
| 0056 | FileManager interactive approval — agent requests, human approves destructive op | Accepted | ||
| 0057 | RAG Microsoft Word (.docx) ingestion | Accepted | ||
| 0058 | FileManager pending-approval TTL — stale destructive requests expire | Accepted | ||
| 0059 | Planner brief "today's plan" headline via a synthesis slot | Accepted | yes | |
| 0060 | Finance source-of-truth — authority, identity-keyed dedup, bounded agent reasoni | Accepted | ||
| 0061 | Finance document storage & retention policy | Accepted | private tree | yes |
| 0062 | — FileManager as custodian: the File Catalog + custody API (FMX0) | Accepted | yes | |
| 0063 | — Finance as the first FileManager custody client (FMX1) | Accepted | yes | |
| 0064 | — FileManager media + iCloud Drive indexer (FMX2) | Accepted | yes | |
| 0065 | — FileManager Apple Photos library indexer (FMX3) | Accepted | yes | |
| 0066 | — RAG documents in the catalog; custody scope for other writers (FMX4) | Accepted | yes | |
| 0067 | — FileManager never deletes files it doesn't own (purge tier guard) | Accepted | yes | |
| 0068 | — Curator-driven tier-escalation judge (model cascade) | Accepted | yes | |
| 0069 | System Health — a harness capability with local-on / network-opt-in probing | Accepted | evarness/services/health/, sdk/health.py, harness_api/health_routes.py |
yes |
| 0070 | Sandbox replay-eval harness for learning experiments | Accepted | runtime/self_learning_loop.py, tools/sandbox/ |
yes |
| 0071 | Semantic email retrieval + dues-from-notification extraction | Accepted | evarness_personal/email/semantic_index.py, agent_tools.py |
yes |
| 0072 | Judge-gated, optional feedback & clarify loop | Accepted | yes | |
| 0073 | Pending actions — background jobs surface actionable blockers as tasks, with an | Accepted | services/tasks/pending_actions.py, runtime/action_center.py, harness_api/action_center_routes.py |
yes |
| 0074 | Per-agent console — an ops dashboard per registered agent | Accepted | agent/agent_metadata.py, runtime/agent_console.py, harness_api/agent_routes.py |
yes |
| 0075 | Calendar as the single write authority (events, invites, appointments, reminders | Accepted | private tree | yes |
| 0076 | In-chat approval for consequential actions | Accepted | yes | |
| 0077 | Converge on one governed agentic loop — tools as universal actionable items | Accepted | yes | |
| 0078 | Generic surface-feedback + suppression spine | Accepted (PRD log) | services/learning/suppression.py, harness_api/session_routes.py, runtime/react_tools.py |
yes |
| 0079 | Window-aware conversation auto-compaction | Accepted (PRD log) | yes | |
| 0081 | Context-health surface (one view of context management) | Accepted (PRD log) | agent/context_health.py, runtime/session_memory.py, cli/context_health.py |
yes |
| 0082 | Learn before you forget (mine the compaction-archived span) | Accepted (PRD log) | yes | |
| 0083 | Learning experiment console (hot-toggle, no restart) | Accepted (PRD log) | yes | |
| 0084 | Act on context-health (on-demand compaction) | Accepted (PRD log) | yes | |
| 0085 | Measure the memory garbage-in / garbage-out guarantees | Accepted (PRD log) | yes | |
| 0086 | Agent self-management tools (act on your own brain) | Accepted (PRD log) | yes | |
| 0092 | Portfolio answered locally — kill the web-search name leak | Accepted (PRD log) | ||
| 0097 | Portfolio vs market — live index context, not a fake benchmark | Accepted (PRD log) | yes | |
| 0101 | Apple Calendar read-back — IRIS can finally see your schedule | Accepted (PRD log) | private tree | yes |
| 0102 | Personal-finance questions never touch the web — dues intercept + research guard | Accepted (PRD log) | yes | |
| 0103 | Action requests escalate to a capable model (generic, not finance-specific) | Accepted (PRD log) | yes | |
| 0105 | User-fact store coherence — single write-seam + reconcile doctor, no DB merge | Accepted | yes | |
| 0106 | Session-owned continuations — every pending question has an owner | Accepted | yes | |
| 0107 | The PostStep evaluator does not run on the streaming loop | Accepted | yes | |
| 0108 | The approval queue, connected — and a channel for the web | Accepted | yes | |
| 0109 | The layered tree — src/iris_harness as the harness's building blocks |
Accepted | ||
| 0110 | Tool effects and loop guidance are declared in the manifest; the loop mechanics | Accepted | yes | |
| 0111 | The planner engages for cross-agent or parallel work, and hands results downstre | Accepted | yes | |
| 0112 | Reminder text is summarised, dates are resolved on the user's clock, a clarifyin | Accepted | ||
| 0113 | The finance agent forms the user's accounts and card due cycles from the evidenc | Accepted | yes | |
| 0114 | Memory is what the owner confirmed, and context is what the prompt actually carr | Accepted | yes | |
| 0115 | Memory is a graph of claims, and its engine (memris) knows no vocabulary | Accepted | yes | |
| 0116 | The health watch repairs what it can, then asks the owner — and remembers | Accepted | yes | |
| 0117 | Paired devices hold their own tokens; the shared secret is for services | Accepted | yes | |
| 0118 | A destructive tool call is approved by code, per call, through the approval queu | Accepted (implemented) | kernel/governance/plugins/destructive_approval.py, plugin_host/manifest.py |
yes |
| 0119 | Removing something from memory is reversible, and it reaches the agent | Accepted (implemented) | memory/removal.py, memris/graph.py, harness_api/memory_routes.py |
yes |
| 0120 | Settings the app changes live in one store on the data volume | Accepted | yes | |
| 0121 | Finance reads trusted mail in full and answers card questions from records | Accepted | yes | |
| 0122 | The loop delivers, or it is not proven | Accepted (implemented) | services/routines/seeded.py, foundation/clock.py, config/heartbeats.yaml |
yes |
| 0123 | Apple Foundation Models as a local model provider | Proposed (not implemented) | ||
| 0123 | Models are chosen by capability, not by tier size | Proposed (not implemented) | ||
| 0125 | Owner-PII masking across every guard | Accepted | yes | |
| 0126 | The public tree carries no owner and no deployment | Accepted | ||
| 0127 | A turn the system opens, and the first-chat welcome | Accepted | yes | |
| 0128 | No bundled trace UI: OTLP to the user's backend | Accepted | yes | |
| 0129 | HarnessServices field freeze + a pinning test | Accepted | ||
| 0130 | Capability catalogue stays closed for 0.x | Accepted | ||
| 0131 | Canonical store + rebuildable projection is a standing rule, not a one-off fix | Accepted | ||
| 0132 | A durable PRE_TOOL_USE intent record for high-risk/destructive actions | Accepted | ||
| 0133 | A party field on PluginManifest, distinct from the existing trust field |
Accepted | ||
| 0134 | PluginManifest.party defaults to untrusted |
Accepted | yes |