# v0.90.0 — Freshness Controller & Self-Tuning

> **Status:** Implemented (advisory controller; authority rollout remains gated by evidence)
> **Scope:** Large
> **User promise:** *"Tell me whether my freshness target is feasible and how pg_trickle would meet it."*
> **Blocked by:** [v0.89.0](v0.89.0.md)
> **Renumbered:** previously v0.85.0. The user-facing `target_freshness`
> control moved forward to [v0.86.0](v0.86.0.md). This release makes the
> measurement authoritative and introduces the controller in advisory mode.

## Theme

Keep `target_freshness` as the primary product control, but make automation earn
authority with benchmark and soak evidence. The first controller reports what
it would do and why. It does not need to own every scheduling and execution
choice before v1.0.

The operating objective is explicit:

> Meet feasible p95 freshness targets while minimizing pg_trickle resource use
> and preserving configured OLTP headroom. When all targets cannot be met,
> minimize weighted SLA breach instead of increasing resource use without a
> bound.

## Items

### SLA-1: Authoritative freshness measurement

`target_freshness` keeps the interval, `on_commit`, and `manual` forms accepted
in v0.86.0. For an interval target, pg_trickle reports the p95 age of visible
data from source commit to refresh commit. Missing provenance is reported as
missing evidence, not replaced with refresh duration.

`pgtrickle.freshness()`, `pg_stat_pgtrickle`, `health_check()`, Prometheus, and
OpenTelemetry expose the target, p50, p95, p99, breach duration, evidence age,
and feasibility status.

### SLA-2: Reproducible advisory decisions

For each stream table, the controller recommends:

- refresh timing
- DIFFERENTIAL or FULL
- pipeline batch size
- queue priority
- worker demand
- overload deferral

`explain()` and internal telemetry show the recommendation, the input evidence,
and the reason. The same input snapshot must produce the same recommendation.
An advisory decision cannot mutate configuration or override an explicit user
setting.

### SLA-3: Layered controller

The design has three layers with separate responsibilities.

| Layer | Responsibility |
|-------|----------------|
| Global resource controller | Determines the CPU, memory, worker, and connection capacity pg_trickle may use without violating the configured OLTP headroom. |
| SLA scheduler | Allocates available capacity by target, current freshness, breach risk, priority, dependency order, and fairness. |
| Per-stream execution controller | Recommends refresh mode, batch size, and other execution choices within the resource allocation. |

One layer cannot compensate for another by exceeding its bound. Infeasible
targets cannot monopolize workers or force the global controller into a resource
race.

### SLA-4: Authority rollout

Automation advances in this order:

1. advisory decisions
2. automatic refresh timing
3. automatic priority
4. automatic batch sizing
5. adaptive worker allocation
6. automatic FULL or DIFFERENTIAL selection

Advisory mode is mandatory for v0.90.0. A later stage may become authoritative
in the same release only when burst, steady-state, restart, and mixed-SLA tests
show stable behavior with no unsafe regression. Each decision class has its own
enablement gate. A failed gate leaves that class advisory and does not block the
v1.0 lifecycle critical path.

### SLA-5: Feasibility, overload, and oscillation

The controller reports an infeasible target when measured work cannot meet it
within the configured resource policy. Under overload, foreground PostgreSQL
work wins. pg_trickle defers lower-risk refreshes, reports the expected breach,
and keeps fairness and durability constraints intact.

Hysteresis or an equivalent bounded policy prevents repeated switching. Tests
cover burst traffic, steady inflow, workload shifts, restart, and an infeasible
mix of targets.

### SLA-6: Configuration audit

Classify each scheduler and execution setting as a primary control, an advanced
override, or internal state. Do not remove an override until the replacement
decision class has earned authority. `explain()` reports both the recommendation
and any override that prevents it.

## Exit criteria

- [ ] Freshness measurement is authoritative and preserves exact source commit
      provenance through stream-table chains
- [ ] Every controller recommendation is visible, reproducible, and backed by
      current evidence
- [ ] The three controller layers have independent bounds and tests
- [ ] Burst, steady-state, restart, and mixed-SLA tests show no oscillation or
      unbounded worker demand
- [ ] OLTP headroom wins under overload. Infeasible targets are reported and do
      not monopolize refresh capacity
- [ ] Any automated decision class has its own benchmark, soak, rollback, and
      inspection gate. Unvalidated classes remain advisory
- [ ] `pgtrickle.freshness()` and `sla_status` are documented and exported to
      Prometheus and OpenTelemetry
- [ ] Configuration is classified without removing a control whose automated
      replacement remains advisory
