All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
2.2.0 - 2026-10-08
Fixed
Every battery reported
trebejo not loadedinstead of doing its job.Disk,Memory,PostgreSQLandRediseach carried their own copy of asafe_run_cmd_legacy/3wrapper that shelled out viaTrebejo.Util.run_cmd_legacy/3when that module was loaded and otherwise returned the literal placeholder{"trebejo not loaded", 127}.trebejois deliberately not indeps(botica has to resolve and build without it), so the guard was always false and the graceful-degradation path was the only path — a health check reporting "trebejo not loaded" instead of disk usage, memory pressure, or whether postgres and redis are actually up.New
Botica.Batteries.Command.run/3keeps the Trebejo branch for anyone who does have it loaded and falls back toSystem.cmd/3otherwise, so a battery always returns a real reading. The four duplicated private wrappers now delegate to it.The fallback also handles the two things
System.cmd/3does not do thatTrebejo.Util.run_cmd_legacy/3did: it has no:timeoutoption (the call is wrapped in a task sosudo systemctlcannot block on a password prompt forever, yielding124on expiry), and it raises when the binary is not onPATH(mapped to127, the shell's "command not found", so the existing exit-code classification keeps working).Two long-failing tests in
test/botica/batteries_test.exs—Disk.check_disk/3andMemory.check_memory/2"result message includes % used" — had been red since the batteries were introduced, because they assert on the real reading rather than on the placeholder.
Changed
- Sibling deps resolved from Hex at their latest releases:
aperois now{:apero, "~> 4.0"}andarreais{:arrea, "~> 3.1"}(wasgithub: "Lorenzo-SF/apero"/github: "Lorenzo-SF/arrea"). This reverts the GitHub-pinning introduced inb3bd2bband puts the published package back on the registry, so consumers ofboticaget the released lines instead of tracking siblingmainbranches.mix.locknow resolvesapero 4.1.0,arrea 3.1.0and transitivelyalaja 3.2.0,batamanta 3.1.0,pote 3.0.0from Hex. trebejostays out ofdepson purpose — the code usesCode.ensure_loaded?(Trebejo.…)guards to degrade gracefully when it is absent, soboticaresolves and builds without it. The commented-out line is refreshed to{:trebejo, "~> 2.1"}for whoever re-enables it.source_refaligned to2.2.0(was2.1.1).mix formatran repo-wide, adding the missing trailing newline to 14 files thatmix format --check-formattedwas already rejecting.
2.1.1 - 2026-09-18
Note:
2.1.0was published on hex.pm from pre-release state and can no longer be replaced (hex allows--replaceonly within one hour). This2.1.1carries the full 2.1.x content; pin to~> 2.1to get it.
Fixed
Botica.Runner.Executor.execute_single_check/2— three real runtime bugs in the spawn/recv loop:- The check worker was spawned with
[:link, :monitor], so a check that calledexit/1(e.g.exit(:boom)) propagated the exit signal to the caller and killed it. The link is removed; the check still runs in a monitored, unlinked process. - Successful and rescued checks never called
Process.demonitorwith[:flush], so:DOWNmessages leaked into the caller's mailbox — one per check in the sequential path. They are now always demonitored and flushed. - The
{:check_result, _}message was untagged, so a late result from a timed-out check could be consumed by the next sequential check'sreceive, returning the wrong result. The message is now tagged with a freshmake_ref/0and stale leftovers are drained after the kill.
- The check worker was spawned with
Botica.Validation.validate_config/1— now rejectschecks: []. Previously the validation passed andExecutor.executecomputedmax_concurrency: 0, which madeTask.async_streamraiseArgumentError.
Added
Botica.Flags.Store.stats/0— public API that returns%{writes: n, count: n}. Thehandle_call(:stats, ...)was previously orphaned.@docfor 17 previously undocumented public exports acrossBotica,Botica.Flags,Botica.Flags.Store,Botica.Flags.Doc,Botica.Flags.Config,Botica.Runner.Executor, andBotica.Doctor.- Regression tests for the five runtime bugs in
test/botica/runtime_bugs_test.exs(11 tests). Botica.Flags.Config— reads default flag definitions from application config (config :botica, :flags).Botica.Flags.Doc— generator that writesdocs/FLAGS.mdwith a markdown table of all registered flags.botica:config— mix alias to run the doc generator.Botica.Flags.Storenow auto-loads defaults on start and emits[:botica, :flags, :put]/[:botica, :flags, :delete]telemetry events.
Changed
mix.exs:source_refaligned to2.1.0(was3.0.0, pointing at a non-existent tag). Documented the rationale for path-only sibling deps.Botica.Flags.Store.put/1/delete/1now use an explicit5_000msGenServer.calltimeout.
2.0.0 - 2026-07-07
This entry consolidates everything between 1.0.0 and the current
HEAD — including the feature flags module, the production hardening
pass, the four Batteries migrations to Arrea (PostgreSQL, Redis,
Memory, Disk), the Botica.Runner.* / Botica.Repair.Fixer
additions, and the dialyzer fix in Batteries.Redis. Earlier
0.x versions are no longer maintained and have been collapsed
into this single canonical 2.0.0 entry.
Added
Botica.Flags— feature flags with ETS backend and deterministic per-entity rollouts. SeeBotica.Flagsfor full API.Botica.Flags.Store— GenServer that owns the ETS table.Botica.Flags.Flag— struct with timestamps +Flag.new/2factory that clamps rollout to 0..100.Botica.Application— new OTP application that starts the supervisor tree (Botica.Flags.Store).Botica.Doctor.flags_summary/0—[:count, :flags]snapshot.Botica.Doctor.format_flags_summary/0— formatted banner for the Doctor's diagnostic output:Flags (3 defined): ✓ beta_search enabled (default: true) ✗ new_dashboard disabled (default: false) ~ rate_limiting rollout 25% (default: false)- 12 defdelegates on the top-level
Boticafacade. Botica.Runner.Executor,Botica.Runner.Sequencer— orchestrate and run check sequences, with@max_default_concurrencyas aTask.async_streamcap (DoS hardening).Botica.Repair.Fixer— fix-step runner that pairs with each check's:fixcallback.Botica.Batteries.PostgreSQL— health check migrated to Arrea (BOTI-101). UsesCommand.execute/2and validates against empty-output edge cases.Botica.Batteries.Redis— health check migrated to Arrea (BOTI-102). Prefersredis-cliwhen available, falls back toApero.Network.port_open?/3for raw TCP probes.Botica.Batteries.Memory— health check migrated to Arrea (BOTI-103).Botica.Batteries.Disk— health check migrated to Arrea (BOTI-104). ForcesLC_ALL=Csodfparser always sees English column headers regardless of host locale.
Fixed
Botica.Runner.Executor: was unbounded —Task.async_streamwith no concurrency cap could spawn hundreds of workers on a large check list. Now capped at@max_default_concurrency.Botica.Batteries.PostgreSQL.check_connection/3was matching on empty output (stdout == "") as a success case, which is impossible for a realpsqlinvocation. Removed the impossible clause.Botica.Batteries.Redis.start_service/0— thewithstatement used:ok <- try_start_commands(), buttry_start_commands/0always returns a 2-tuple ({:ok, _} | {:error, _}), never the bare atom:ok. Dialyzer flagged this aspattern_match The pattern can never match the type. The success branch was therefore unreachable; in the failure case theelseclause caught the tuple but the runtime path was broken. Changed to{:ok, _} <- try_start_commands().start_service/0now correctly returns{:ok, "Redis service started"}when the service was successfully started.Botica.Runner.Executor— removed self-referencing alias that broke compilation.Botica.Runner.Executordecoupled from Arrea (uses Alaja facade instead) to avoid the dep cycle.Botica.Doctor— Task supervisor production hardening: sudo permission check before systemctl calls, lower default concurrency.source_refinmix.exsnow points to the canonical2.0.0tag (was1.0.0). The danglingvprefix in the CHANGELOG footer was also dropped.
CI
- Multi-stage CI: format → credo → sobelow → test+coverage → dialyzer.
workflow_dispatchadded for manual re-runs.- Sobelow step dropped (Phoenix-only scanner, false positives on libs).
- Credo
--strictrelaxed to match apero/candil config.
Tests
- 27 tests in
test/botica/flags_test.exscovering define/enable/ disable/set/delete, rollout bucketing (deterministic, uniform distribution, 0% / 100% edges,for:ignored when rollout is nil),all/0sorting,count/0,Flagstruct (rollout clamping), and Doctor integration. - 35 tests in
test/botica/batteries_test.exscovering all four Batteries (PostgreSQL, Redis, Memory, Disk) — check + start_service paths.
1.0.0 - 2026-06-10
Added
- Initial open source release: feature flags, task supervisor.
A note on history
The git history of this repository was rewritten as part of a deliberate cleanup effort. The commits you can read describe the codebase as it stands today — they do not preserve the original chronology of development.
Anything worth keeping from before the rewrite was carried forward as tagged releases with explicit
CHANGELOG.mdentries. Anything not preserved is, by the maintainer's choice, no longer part of the canonical development line.Tag
1.0.0points to the initial open-source cut-over; tags2.0.0and2.1.0(current HEAD) point to their respective releases. All versioned artifacts on Hex.pm and GitHub Releases follow this convention.