132 lines
8.1 KiB
Markdown
132 lines
8.1 KiB
Markdown
# DECISIONS.md — ADR log
|
|
|
|
Decisions inside the set architecture. D-NNN, never renumbered.
|
|
|
|
- **D-001** — SDD+BDD+TDD adopted (2026-07-13). SPEC-system port:
|
|
numbered requirements in `specs/`, coverage classes
|
|
feature/test/manual, `tools/trace.py` enforcement in `make check`.
|
|
Process is binding — see SPEC-000.
|
|
- **D-002** — BDD runs at the responder seam, not against live
|
|
services. `FakeModelResponder` scripts model output; Discord-event
|
|
behavior is unit-tested with mocked discord.py objects. Rationale:
|
|
Discord ToS forbids test-account automation and LLM output is
|
|
nondeterministic — a live-BDD lane would be flaky by construction.
|
|
- **D-003** — Packaging = uv + pyproject only (2026-07-13). setup.py,
|
|
requirements.txt and pytest.ini removed; single source of truth,
|
|
locked via uv.lock. Python `>=3.11` floor keeps the kitchen host
|
|
(py3.11) deployable until FDB-016 lands.
|
|
- **D-004** — openai SDK pinned `<2` (1.109.x). The v2 SDK migration
|
|
happens together with the FDB-005 envelope rewrite (structured
|
|
outputs / Responses API) — one breaking change, one test cycle,
|
|
instead of two.
|
|
- **D-005** — `--strict-markers` stays on; requirement tags from
|
|
`.feature` files are registered as pytest markers dynamically in
|
|
`tests/conftest.py`.
|
|
- **D-006** — FDB-005 stays on chat.completions + structured outputs;
|
|
the Responses API migration is deferred to FDB-007, where history
|
|
handling gets redesigned anyway — one conversation-state reshape
|
|
instead of two.
|
|
- **D-007** — openai SDK bumped to 2.x together with the envelope
|
|
rewrite (supersedes the D-004 pin). `multiline` dependency dropped —
|
|
strict schema output made relaxed-JSON parsing dead code.
|
|
- **D-008** — Operator runtime flags (pause/images/tasks/quiet) are
|
|
in-memory only; a restart resets to config defaults. Persistence
|
|
arrives with the FDB-011 task store if staff practice demands it.
|
|
- **D-009** — `translate()` still keys off `fix-model` although the
|
|
repair path is gone; the whole translate-before-draw step dies in
|
|
FDB-009 (gpt-image-2 is multilingual). Not worth a config rename
|
|
for one phase. *(Closed 2026-07-13: FDB-009 deleted translate();
|
|
the fix-model config key is now fully dead and can be dropped from
|
|
live configs.)*
|
|
- **D-010** — Persistence uses stdlib `sqlite3` via
|
|
`asyncio.to_thread`, not aiosqlite: no new dependency, and a
|
|
connection-per-operation with WAL is plenty at this message volume.
|
|
- **D-011** — `save_history` replaces the channel's rows wholesale
|
|
per message instead of appending: histories are capped at
|
|
`history-limit` (~200-350 rows) and trims must be reflected;
|
|
correctness over micro-optimization.
|
|
- **D-012** — Budget spend is *estimated* from configured per-token/
|
|
per-image prices, not fetched from the billing API: deterministic,
|
|
testable, no extra scopes. Dashboard hard limits (FDB-001) stay the
|
|
outer safety net; this ledger is the inner, immediate one. User
|
|
image quota counts at grant time (reservation), global image spend
|
|
at generation time.
|
|
- **D-013** — `!forgetme` v1 purges history rows only; the
|
|
single-string channel memory cannot be selectively cleaned. Full
|
|
fact-level erasure ships with FDB-007 structured memory — stated in
|
|
the user-facing confirmation, not hidden. (Superseded by D-014:
|
|
erasure now covers facts, observations and episode traces.)
|
|
- **D-016** — The reply/ignore classifier fails open (BEH-03): a
|
|
broken classifier must never mute the bot; the budget gate already
|
|
bounds spend. Its verdict gates BEFORE the main call, the
|
|
envelope's answer_needed still gates after — two independent nets.
|
|
- **D-021** — Health monitoring (FDB-012, SPEC-012 OPS-18/19): a
|
|
separate `monitor_loop` (own cadence, default 300 s) rather than
|
|
folding checks into the 60 s task loop — monitoring is coarse and
|
|
should not run every minute. Checks are edge-triggered (alert on the
|
|
rising edge, re-arm on recovery) so a standing condition never spams;
|
|
they reuse the existing rate-limited staff-alert path. Metrics are
|
|
the cheap, high-signal ones (spend vs budget, free disk, task-queue
|
|
depth); each is independently skippable when it has no data, so a
|
|
deployment without a budget or store still runs the others. Opt-in
|
|
(`enable-monitoring`) like every other operational rollout.
|
|
- **D-021** — Responses API behind `use-responses-api` (FDB-028,
|
|
ENV-22..24, resolves D-006): the responder path can use
|
|
`/v1/responses`, which allows tools + `reasoning_effort` (the
|
|
chat/completions 400 from ENV-21) and keeps one chain of thought
|
|
across tool rounds. Stateless by choice: `store=false` +
|
|
encrypted reasoning items passed back — GDPR posture unchanged, no
|
|
server-side conversation retention. Flag defaults off; rollback is
|
|
a config toggle (hot-reload), not a deploy. Classifier /
|
|
consolidation / task-gen stay on chat/completions (no tools, no
|
|
reasoning need — not worth the churn).
|
|
- **D-020** — Web search via Exa (FDB-022, SPEC-015): a `web_search`
|
|
tool alongside fetch_url/IGDB/codex/get_news, filling the "look it up
|
|
on the open web" gap. Exa (not a raw search-engine scrape) because it
|
|
returns clean title+url+text in one call — no SSRF surface of our own
|
|
(we call one fixed API endpoint, not arbitrary hosts), and it pairs
|
|
with fetch_url for the full article. Key is a host secret
|
|
(`exa-api-key`, env `EXA_API_KEY` fallback), never repo-side; results
|
|
sanitized like every other external-text tool; off by default
|
|
(`enable-web-search`), metered per user.
|
|
- **D-019** — News memory + on-demand tool (SPEC-013 NEWS-07..12):
|
|
the news pipeline now carries item summaries (feed descriptions,
|
|
HTML-stripped) and persists every fetched item into a deduped `news`
|
|
table (schema v6), pruned to a rolling window (`news-keep`). Both the
|
|
kroa digest run and the ggg posting run write to it, so the store is
|
|
a single searchable source across both models. A `get_news` tool
|
|
reads that store (topic/source-filtered, metered, sanitized) rather
|
|
than re-fetching feeds live: the ambient `{news}` digest stays a
|
|
small always-on snapshot, while the tool gives unbounded on-demand
|
|
reach without a fresh network round-trip per call. The store is the
|
|
same `bot.db` (WAL) the bot uses; the cron process opens it
|
|
independently — concurrent reader/writer is what WAL is for.
|
|
- **D-018** — Codex Mechanicus search (FDB-019, SPEC-014): Luma's
|
|
lore is grounded in the priest's real archive at binaric.tech via a
|
|
`codex_search` tool over the site's public `search-index.json`, not
|
|
a bot-side copy — the index stays a single source of truth, refreshed
|
|
by the site's own publish rite, and the bot caches it in memory
|
|
(TTL). It reuses SPEC-011's `guard_url` + `read_capped` (fetch is
|
|
SSRF-guarded and byte-bounded) and sanitizes every returned field:
|
|
one's own web content is still untrusted by the time it reaches a
|
|
prompt. Luma-only (`enable-codex`, off elsewhere) — the Adeptus
|
|
Mechanicus archive has no place in Fjærkroa's café persona.
|
|
- **D-017** — All human-behavior knobs default to off/v3.0.0
|
|
semantics; behavior changes are config rollouts per deployment, not
|
|
code flips. The classifier's `factual` flag is the only coupling
|
|
(delay bypass) and defaults to false without a classifier.
|
|
- **D-015** — Deploys are push-based from the dev machine
|
|
(`git archive <tag> | ssh`), not pull-based: no deploy keys or git
|
|
state on the hosts, the artifact is exactly the tag tree, untracked
|
|
live config survives in-place extraction. Trade-off: deploys need
|
|
the dev machine; acceptable for a one-operator project.
|
|
- **D-014** — Structured memory (FDB-007): observations are the only
|
|
consolidation feed (independent of history trimming); consolidation
|
|
returns NEW facts only (no wholesale rewrite — the lossiness of the
|
|
old memoize path is exactly what we're removing); self-authorship
|
|
is enforced in code (fact subject must be an observation author),
|
|
not just in the prompt; memory reads run on the loop (small indexed
|
|
SQLite queries), writes off-loop. Legacy memory strings survive as
|
|
episodes; the memory table stays as a read-only legacy fallback for
|
|
deployments without memory-model.
|