adopt the tracing log facade, engine crates only (review 004 item 7's family-level decision, ADR-025): the family precedent checked and unanimous (alkcall/alktunnels/alksocks/alktty all on tracing = "0.1" facade-only, tracing-subscriber env-filter a dev-dep where present, silent by default); user sign-off taken in-session (2026-10-11, adopt-tracing option chosen) and recorded in the ADR's status; core stays dep- and emit-free (the explicit core-dep answer — the payload serde deps remain the sole core deps, the ADR states the ADR-020-§4 reading explicitly); all 19 library-path eprintln! sites migrated to tracing::warn!/error! with message text + the alkstore: prefix + the secret-free discipline unchanged (17 at review time, +2 from the review's own inline tick/lease restructures — re-grepped, sqlite 10 / pg 9, zero remainder, tests' harness-skip prints untouched); levels: warn = retried/degraded ops, error = unrecoverable loss (watcher death, discarded pooled clients, panic-path rollback failures); the four substrate-lineage files ride PROVENANCE delta D-41 (message-identical macro swap); deployment.md gains the diagnostics-posture section (operator recipe RUST_LOG=warn/error at the consuming binary; listener application_name diagnosability joins it), ADR index + design-decisions table carry the 025 row, CHANGELOG's 0.1.0 body states the facade; pg-fix-forwarder-diagnosability's output form now pinned (warn on failed reconnect connects with attempt/backoff context, error on the loop-exit poll error); gates green: build, workspace tests server-less, clippy -D warnings, fmt, doc gate zero warnings, fuzz corpus replay 6/6; task -> completed

This commit is contained in:
glm-5.3-flash committed 2026-10-11 14:19:07 +00:00
1 parent ce5da852ca
commit a9b6cf0c86
19 files changed
+332 -31

No files matched your search

+1
View File
@@ -76,6 +76,7 @@ publishing.
| [022](decisions/022-contract-suite-layout.md) | Contract-suite layout — shared internal suite crate, properties parameterized over a store factory (discharges ADR-017 §4.2's deferral) | Accepted |
| [023](decisions/023-fourth-review-round.md) | Fourth review round — `encode_payload` typed (`Codec`), numeric-argument domains by kind (extents clamp empty, durations reject, boundaries total), plain-path SQLite open (URI flag dropped), watcher cadence 1 ms default + `SqliteOpts` knob | Accepted |
| [024](decisions/024-mem-engine.md) | The mem engine — a third engine (`alkstore-mem`), full contract v1, honest-ephemeral posture (collapses + amends ADR-001 §4's deferral) | Accepted |
| [025](decisions/025-log-facade.md) | Log facade — `tracing` adopted in the engine crates, silent by default, subscriber wiring on the consumer/binary; core stays dep- and emit-free (review 004 item 7's family-level decision) | Accepted |
## Open Questions
@@ -0,0 +1,183 @@
# ADR-025: The log facade — adopt `tracing`, engine crates only
## Status
Accepted (2026-10-11, Phase 1 — the family-level posture decision
that `docs/reviews/004-pre-release-general-review.md` §"Findings
recorded, not fixed" item 7 explicitly recorded rather than made: the
review's item 7 named the posture gap (no facade, 17 library-path
`eprintln!` sites carrying the diagnostics) and left the
`log`/`tracing`/`eprintln!` call as a family-level decision. The
class-1 amend-in-place window ([ADR-017](017-contract-versioning.md)
§2) is still open — no versioned artifact exists — so the adoption
lands pre-release, before the consumer task (the pg forwarder
diagnosability fix) needs the output form. **User sign-off recorded
2026-10-11: adopting `tracing`, this option chosen by the user in
session** — the sign-off is part of this record per the deciding
task's acceptance criterion.)
## Context
The workspace ships four published crates with diagnostics but no log
facade: the engine crates emit operator-visible failures through
`eprintln!` — unconditional, un-filterable, un-routable stderr writes
whose verbosity the consumer cannot turn down and whose presence the
consumer didn't necessarily ask for. The review verified all sites
secret-free (no DSN/password material ever printed) and recorded the
posture question rather than deciding it.
Three forces frame the decision:
1. **The family precedent is unanimous.** All four family repos were
checked this session (2026-10-11): alkcall, alktunnels, alksocks,
and alktty each depend on `tracing = "0.1"` in their library
crates, emit through `tracing::{error,warn,info,debug}` macros,
and carry no subscriber in `[dependencies]` — where
`tracing-subscriber` (env-filter) appears at all (alktunnels,
alksocks) it is a **`[dev-dependencies]`** entry, so the
integration tests can mount a sink and assert on emissions; the
subscriber wiring in production is the consuming binary's/app's
business. Nobody in the family uses the `log` facade.
2. **Core's dependency posture constrains the shape.** Core
([ADR-001](001-crate-split.md) split;
[ADR-020](020-enqueue-opt-semantics-and-bridges.md) §4's
dependency posture, serde/serde_json being the sole core deps)
must not grow a facade dependency — a dep on `alkstore` is a
contract-posture change and buys nothing: core has **zero**
`eprintln!` sites (re-grepped) — core emits nothing and needs to
emit nothing; the diagnostics live in the engines. The adoption
is therefore *engines-only*: lightest shape, contract untouched.
*(Amendment note: ADR-020 §4's text is about payload encoding and
says nothing about logging; the constraint applied here is
ADR-020 §4's dependency line read with
[core-contract.md](../core-contract.md) — the payload serde deps
are the sole core deps. This ADR states the reading explicitly so
the log decision doesn't silently stretch an encoding ADR beyond
its text.)*
3. **Published-crate consumer expectations.** A facade that emits
nothing until the consumer installs a subscriber is the
consumer-respectful posture — silent by default, opt-in filtering
(`RUST_LOG`-style env-filter at the wiring site), structured
fields, zero output cost when compiled without a subscriber. The
`eprintln!` posture forces stderr noise on every consumer.
`tracing`'s no-subscriber overhead is a cheap disabled-event call
— invisible in the seam-latency budgets
([ADR-007](007-transactional-seam.md)'s measured postures).
The deciding task also named the disposition question: what happens
to the existing `eprintln!` sites under whichever option lands.
## Decision
**Adopt `tracing` as the log facade, in the engine crates only.**
Core keeps its dependency posture untouched — no facade dep on
`alkstore`, ever (the engines-only shape is the contract-safe one;
moving diagnostics *into* core would be a separate decision with its
own record anyway).
- **Dependency shape (mirrors the family, verified above):** each
engine crate that emits (`alkstore-sqlite`,
`alkstore-postgres`) gains `tracing = "0.1"` in
`[dependencies]`; `tracing-subscriber` (env-filter) appears only
under `[dev-dependencies]`, where a test needs to assert on
emitted events. No subscriber is wired inside any library; the
consuming binary/app owns that wiring (the family's uniform
posture).
- **Core: silent.** Zero `tracing` (or `log`) dependency; zero
emission sites; the contract's failure reporting stays the typed
error taxonomy ([core-contract.md](../core-contract.md)). This is
the explicit answer to the core-dep question the deciding task
required.
- **Semantics of an event without a subscriber:** dropped — no
output, no noise. *Silent by default, loud on opt-in* is the
facade's standing guarantee and the operator-facing property this
ADR pins.
- **The secret-free rule is the posture of record:** no DSN,
password, token, connection-credential material, or
consumer-table content in any engine event — the review's verified
property promoted to standing rule any new emission site inherits.
Diagnostics name mechanisms, error classes, backoff/attempt state,
and engine-owned identifiers (channel names, queue names, listener
`application_name`-class labels), never credentials.
- **Site disposition: migrate all.** The review counted 17
library-path `eprintln!` sites (verified secret-free); the re-grep
at decision time finds **19** — sqlite 10, pg 9 — the delta
arriving from the review's own inline fixes re-structuring the
tick/lease failure arms. All 19 migrate from `eprintln!` to the
`tracing` facade in this adoption (levels: `warn` for
retried/degraded operations and failed diagnostics paths, `error`
for unrecoverable-loss conditions such as watcher death or the
discarded poll error); message text and secret-free discipline
carry over verbatim-class. No site is kept in `eprintln!` and none
is deleted: every emission the review verified carries operator
value the facade preserves. The pg forwarder's swallowed
connect/poll errors (the review's item 7 arms,
[the deciding task's consumer](../../tasks/pg-fix-forwarder-diagnosability.md))
surface through the same facade — `warn` on the failed reconnect
connects with attempt/backoff context, `error` on the loop-exit
poll error.
- **Levels and fields are engine-local detail**, not contract: the
contract's only log-adjacent pin is the error taxonomy's
`Display` shape. Event field names may change without an ADR;
removing *an engine's diagnostics entirely* would be a posture
change (record one).
## Consequences
**Positive**
- The family posture is uniform: one facade across alkcall,
alktunnels, alksocks, alktty, and alkstore's engines; operators
learn one wiring recipe (env-filter subscriber) per family binary.
- Consumers of the published crates get silence by default and
opt-in structured diagnostics — the published-crate expectation
the deciding task named is answered in the consumer-respectful
direction.
- The diagnosability consumer task has its output form pinned
before it runs; the pg forwarder fixes land as `tracing` events.
- The secret-free discipline is now stated law, not inherited
hygiene re-verified per review.
**Negative**
- Two engine crates gain a dependency (the facade only — no
subscriber dep in `[dependencies]`); the dep-graph delta is one
crate, family-adopted four times over.
- Events are invisible until the consumer wires a subscriber —
an operator running a bare integration sees no diagnostics
where the old `eprintln!` posture printed unconditionally. The
deployment matrix states this honestly (operators wire
`RUST_LOG` at the consuming binary; the engine crates' docs say
so).
- No-op-subscriber sites still cost a disabled-event check per
emission — unmeasured here, but the family carries it and the
POC-measured budgets are `Event`-free paths.
## References
- `docs/reviews/004-pre-release-general-review.md` §"Findings
recorded, not fixed" item 7 — the finding (the no-facade posture,
the secret-free verification of the `eprintln!` set, the
family-level framing); the §"What was checked and clean" security
sweep is the secret-free verification of record.
- The family precedent (verified in-session, 2026-10-11):
`alkcall/Cargo.toml:31`, `alktunnels/Cargo.toml:32`,
`alksocks/Cargo.toml:40`, `alktty/Cargo.toml:44` — `tracing`
facade-only; `alktunnels`/`alksocks`' `tracing-subscriber`(env-filter)
entries are `[dev-dependencies]`.
- [ADR-001](001-crate-split.md) (crate split),
[ADR-020](020-enqueue-opt-semantics-and-bridges.md) §4 + this ADR
§Context point 2's explicit reading — core stays
payload-deps-only.
- [ADR-017](017-contract-versioning.md) §2 — the class-1 window this
adoption rides; no contract text moves (no trait, type, or error
variant changes; zero consumer-inventory rows required — nothing
here assumes a consumer need, the diagnostics belong to engines
and operators).
- `docs/architecture/deployment.md` — the operator-facing posture
section this ADR's landing adds.
- [ADR-004](004-postgres-driver.md) — the pg forwarder's
`application_name` diagnosability note, the first
operator-diagnosability posture in the family.
- The deciding task: `tasks/decide-log-facade-adoption.md` (sign-off
recorded in §Status).
+37 -2
View File
@@ -1,7 +1,11 @@
---
status: stable
last_updated: "2026-10-11 (the deployment-matrix release final pass —
every row verified true as implemented across the three engines, the
last_updated: "2026-10-11 (the log-facade decision of record landed —
[ADR-025](decisions/025-log-facade.md): engine diagnostics route
through the `tracing` facade, silent by default, subscriber wiring on
the consumer/binary; the diagnostics-posture section added below.
Earlier the same day: the deployment-matrix release final pass — every
row verified true as implemented across the three engines, the
env-variable boundary pinned, the growth-posture acceptance folded
into the release text, the fixed `synchronous = NORMAL` posture
corrected, internal task/wave pointers removed)"
@@ -168,6 +172,36 @@ consumer-facing surface ever wants env conveniences, the family
pattern is caller-side: the app's config layer reads env and passes
the values into the opts.
## Diagnostics posture
Engine diagnostics route through the **`tracing` facade only, in the
engine crates** ([ADR-025](decisions/025-log-facade.md), 2026-10-11 —
the family posture: all four family repos emit through
`tracing = "0.1"` facade-only). What an operator needs:
- **Silent by default.** An engine emits nothing until a subscriber
is installed — the library crates carry no subscriber and no
`eprintln!`; a consumer running without one sees no engine
diagnostics. This is deliberate (published-crate consumers did not
ask for stderr noise) — but it also means an integration that
*wants* visibility must wire it at the consuming binary.
- **Operator recipe:** a `tracing-subscriber` (env-filter) at the
binary/init point; e.g. `RUST_LOG=warn` for the degraded-operation
warnings (wake failures, retry/backoff, reconnect failures),
`RUST_LOG=error` for the unrecoverable-loss events only (watcher
death, a discarded pooled client). Core never emits anything — the
contract's failure reporting is the typed error taxonomy alone.
- **Secret-free rule of record:** no DSN, password, token, or
credential material in any engine event — diagnostics name error
classes, mechanism identifiers (channel/queue/schedule names), and
backoff/attempt state, never connection material. The review's
verified property, promoted to standing rule.
- **Listener kill-targetability** (pg) still comes from the
`application_name` setting ([ADR-004](decisions/004-postgres-driver.md),
the toolchain row below) — diagnosability joins it: with a
subscriber wired, failed listener reconnects and poll errors surface
as events instead of vanishing.
## Durability knobs
| Engine | Knob | Shape |
@@ -281,6 +315,7 @@ From both POCs (single-box, relative shapes are the deliverable —
| [016](decisions/016-deployment-honesty.md) | Deployment honesty | no runtime capability surface — compile-time identity + this matrix; `PayloadTooLarge` is the one runtime asymmetry carriage |
| [021](decisions/021-tx-reads-and-value-shape-fixes.md) | Third review round | drop = rollback keeps the connection budgets exact under error paths (SQLite lease releases; pg client re-pools) |
| [024](decisions/024-mem-engine.md) | Mem engine | the matrix's third engine — honest-ephemeral: single-process, no durability, never fleet-valid, wasm32 compile-clean; no connection budget, no knobs |
| [025](decisions/025-log-facade.md) | Log facade | the `tracing` facade in the engine crates, silent by default, subscriber wiring on the consumer/binary; core stays dep-free and silent |
## Open Questions