Architecture Decision Records
Binding. Design, code, dependencies and APIs comply with every accepted ADR here. Before proposing or implementing, skim this directory for decisions touching your scope. A new significant decision — adding a technology, changing a pattern, departing from a convention — needs a new sequential ADR before or with the change. Never edit an accepted ADR's decision; supersede it.
Format: NNNN-kebab-title.md, English, Nygard style (000-template.md).
| ADR | Decision |
|---|---|
| 0001 | Name "Artemis Studio"; trademark risk accepted with a cheap rename path |
| 0002 | Jolokia-first transport, Core client second, capability-gated features |
| 0003 | Real-time updates over SSE |
| 0004 | Topology by seed node + auto-discovery |
| 0005 | React 19 + Vite + Mantine 9 |
| 0006 | Own the metrics timeseries in PostgreSQL |
| 0007 | One container image, Docker Compose first |
| 0008 | Liquibase migrations; schema physical tuning |
| 0009 | Secret vaulting with JDK AES-GCM; broker TLS via SSL bundles |
| 0010 | Jolokia over a blocking RestClient; spring-boot-starter-webflux removed |
| 0011 | Persistence via JPA (Hibernate) mapped to the Liquibase-owned schema |
| 0012 | Split-brain detection requires corroborated evidence (amends 0002) |
| 0013 | A cluster is registered from a list of seed URLs (amends 0004) |
| 0014 | Lombok for boilerplate, MapStruct for layer-to-layer mapping |
| 0015 | Tiered scrape scheduler; refresh-cycle counter scheduler-owned per cluster (retires HaRefreshTask) |
| 0016 | queue_snapshot written by JDBC batch upsert, not JPA (scoped exception to 0011) |
| 0017 | Cross-node aggregation — one logical node per NodeID, scrape the live endpoint only |
| 0018 | SSE hub is SseEmitter on Spring MVC, carrying poll-derived change signals (annotates 0003; extended by 0027) |
| 0019 | Frontend API types generated from the backend's OpenAPI document |
| 0020 | The virtualized data grid lays out on shared CSS grid tracks, not <table> |
| 0021 | |
| 0022 | Dry-run is a broker-side estimate; the bulk safety cap is server-enforced |
| 0023 | |
| 0024 | Frontend DOM test harness is Vitest + Testing Library + MSW |
| 0025 | Scrape cadence applies without a restart, via SchedulingConfigurer |
| 0026 | Core client — one subscription per live node, poll loop, Studio-driven reconnect, cached capability verdict (extended by 0031) |
| 0027 | The SSE events topic carries data, with Last-Event-ID replay and coalesced derived signals (extends 0018) |
| 0028 | broker_event — buffered batch insert, bounded queue with a visible drop counter, seq PK |
| 0029 | MessageTransport — Core for read/write fidelity, Jolokia for mutations and deep pages (supersedes 0021) |
| 0030 | Request-reply correlation is notification-anchored and browse-sampled, with a disclosed coverage ceiling |
| 0031 | Pooled Core connections via pooled-jms, superseding connect-per-call |
| 0032 | Request-reply latency via Micrometer time-windowed percentiles, no persisted history in Phase 5 |
| 0033 | Metric read model — date_bin bucketing, gauge vs. counter-rate, server-clamped step/range |
| 0034 | Two-section collapsible sidebar (cluster switcher + per-cluster view nav), replacing the horizontal view strip |
| 0035 | Alert rules are a discriminated union (metric threshold / cluster state); evaluation rides the scrape tiers, not an independent timer |
| 0036 | Notification delivery is a durable Postgres queue, batched per rule per tick, with Standard Webhooks signing |
| 0037 | Session-cookie authentication (not bearer tokens) for the browser, since EventSource cannot set headers |
| 0038 | Fully dynamic permission strings, resolved once per request via a cluster→environment→global scope walk |
| 0039 | API tokens — SHA-256 hash, prefix lookup, grants intersected with the live owner |
| 0040 | OIDC — JIT provisioning, principal swap after the exchange, claim mapping re-applied every login |
| 0041 | Audit actor carries real identity and token attribution (supersedes 0023) |
| 0042 | CalVer releases published to Docker Hub on every push to main (complements 0007) |
| 0043 | Broker configuration is compared by a classified pointer diff, not a diff library |
| 0044 | Slow-consumer detection has two authorities, and the broker wins |
| 0045 | The MCP server is a capability surface (~13 intent-shaped tools), not a REST mirror; the token budget is a build failure |
| 0046 | MCP authenticates with the ADR-0039 personal API tokens — one credential store, one authorization model |
| 0047 | Two configuration planes — studio_setting (operator, live, audited) and a Spring Cloud bootstrap plane (deploy-time, {cipher}); no config server |
| 0048 | Every settings-tunable schedule is a re-reading trigger task, not a @Scheduled annotation (extends 0025) |
| 0049 | Queue and address lifecycle is a cluster-wide fan-out that reports per node, never rolls back, and makes managementWrite evidence-backed |
| 0050 | (superseded by 0054) The MCP listing budget scales per tool and enum/body detail moves to studio://tools (extends 0045) |
| 0051 | The changelog is generated from commit messages, one file per release (supersedes 0042's changelog mechanics) |
| 0052 | One global freshness indicator scoped to observed queries; the stream reconnects forever with a silence watchdog (modifies 0018) |
| 0053 | Broker time is normalised onto Studio's clock; skew is measured from the Jolokia response timestamp, disclosed per flow, and alerted (extends 0030, 0035) |
| 0054 | MCP discovery is the studio_help tool, not an optional resource; one catalogue generates the schemas, help, resource and server instructions (supersedes 0050) |
| 0055 | Metric charts carry time on the x-axis, not array position; a relative window advances quantized to the bucket (extends 0033) |
| 0056 | Every view is bounded and says so; the topology canvas degrades by level of detail above a node threshold (extends 0020) |
| 0057 | Connection identifiers are node-local and ephemeral: a close by id names a node, a vanished target is a success, and the confirmation names the client (extends 0049) |
| 0058 | The SQL Console is a restricted dialect parsed to an AST and validated against a whitelist; predicates split into selector pushdown and residual scan; SELECT-only (extends 0021, 0022) |
| 0059 | The message index is opt-in per queue, retention-bounded, disposable, and treated as retained payload (qualifies 0033) |
| 0060 | A live tail is polled, never consuming or mutating, and states permanently that it is a sample (extends 0058, 0003) |
| 0061 | The documentation site is VitePress under site/, deployed to GitHub Pages by its own Actions workflow; docs/ stays the single source and the README is trilingual |