Ctrl K
ComunidadGuías…

docs/adr/0055-evidence-anchored-value.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# ADR-0055: Evidence-Anchored expectedValue

## Metadata

| Field | Value |
|-------|-------|
| **Status** | Accepted |
| **Date** | 2026-08-12 |
| **Authors** | AlessioBrillo |
| **Deciders** | AlessioBrillo |
| **Supersedes** | N/A |
| **Relates to** | ADR-0002, ADR-0020, ADR-0008, ADR-0006 |
| **Project** | DOMINUS |

## Context

The scoring engine computes `expectedValue = weightedScore × baseMarketValueEur`
where `baseMarketValueEur` defaults to 500 and the market signal saturates at
`score = 1` for any median comparable sale ≥ `highValue` (10,000). The
combination means **no domain can ever be valued above €500** — a domain whose
comparables sell at €200,000 produces the same expected value and buy
recommendation as one with no sales history at all that merely scores 1.0 on
the name-quality signals.

The `suggestedBuyMax` was additionally clamped by `BUY_MAX_ABSOLUTE_CAP`
(default 500), so the maximum possible buy recommendation was €250 regardless
of evidence. The engine's own documentation admitted the cap "flattens the
market signal above ~€500" (`types/score.ts`).

This flattening is not conservatism (ADR-0002: never be *more generous* than
the evidence) — it is a hard ceiling that discards the strongest evidence the
engine collects: the comparable sales median. Every domain above the ceiling
receives the identical recommendation, making the tool useless for exactly the
mid/high-end segment where margins exist.

Two secondary defects surfaced during the same hardening pass:

1. **Checkpoint resume replays stale verdicts.** A run resumed from a
   `pipeline_checkpoints` row written by a different binary version (or simply
   old: DNS/RDAP verdicts go stale) replayed candidate verdicts computed under
   older logic — silently violating ADR-0040 parity.
2. **The Redis heartbeat renewed the wrong lock.** The lock heartbeat ran in
   a `setInterval` callback outside the caller's `runWithTenant` scope, so it
   renewed `pipeline_run:default` while the run held `pipeline_run:<tenant>`,
   then aborted every tenant's controller after 3 failures — a cross-tenant
   outage on the cloud path.

Both are documented in the hardening branch; this ADR governs the value model.

## Decision Drivers

1. **Conservatism doctrine (ADR-0002)** — the engine must never recommend more
   than the evidence supports; "more generous than commercial appraisals" is
   forbidden.
2. **Decision utility** — a tool whose ceiling truncates every recommendation
   at €250-500 is a closeout scalper by construction, not a decision-support
   engine; buyers of 4-5 figure domains get no signal.
3. **Backtest stability (ADR-0008)** — the tuning loop tunes *weights*, not
   the value scale; a change that alters `expectedValue` must not require a
   re-tune of signal weights.
4. **Zero-cost community parity** — the fix must work identically with no
   paid data: no market data ⇒ behaviour must remain exactly as before.
5. **Operator control** — a hard per-deal budget ceiling must remain available
   for operators who want one.

## Considered Options

### Option A: Evidence-anchored value scale (chosen)

`expectedValue = weightedScore × anchorEur` where
`anchorEur = max(baseMarketValueEur, medianSalePrice)` when the market signal
has data, else `baseMarketValueEur`. `BUY_MAX_ABSOLUTE_CAP` default becomes 0
(no ceiling); a positive value still enforces a hard per-deal cap.

**Advantages:**
- Value reflects the strongest evidence (comparables median) instead of
  discarding it; no market data ⇒ byte-identical behaviour to today.
- Weights, thresholds, confidence and `recommended` are untouched — the
  backtest tuning loop (ADR-0008) is unaffected.
- Faithful to ADR-0002: the median is evidence, never generosity; the
  confidence gate (≤ 0.8) still derates bids.
- Operators keep an absolute ceiling via one env var.

**Disadvantages:**
- Domain with a single inflated comparable can inflate the anchor — bounded
  by the existing density adjustment (weight scaling on sparse data) and by
  `suggestedBuyMax = expectedValue × 0.5`.
- Monetary outputs (`expectedValue`, `suggestedListPrice`, bids) change for
  market-data runs; dashboards/consumers see higher numbers than before.

**Cost Implications:** ~0.5 day (engine + config + tests). No infra or API cost.

**Risk Assessment:** Low — the decision path (`recommended`) is unchanged; only
the monetary scale moves. Rollback is a config revert (`BUY_MAX_ABSOLUTE_CAP=500`).

---

### Option B: Logarithmic tier scaling

`expectedValue` is computed via a log-scaled mapping of the median
(generous below the ceiling, gentle above).

**Advantages:**
- Value magnitude compresses outliers smoothly.

**Disadvantages:**
- A new free parameter set (tier boundaries) enters the scoring model,
  requiring backtest validation; tuning the tiers is undeveloped work.
- More complex than the problem needs; the *shape* of the curve is not the
  issue — the flat ceiling is.

**Cost Implications:** 2-3 days incl. tier calibration against NameBio data.

**Risk Assessment:** Medium — model behaviour changes across the whole range,
not just above the old ceiling; backtest re-validation required.

---

### Option C: Keep the ceiling, document it

Keep `expectedValue ≤ 500` and document that DOMINUS is a closeout-scalper
tool.

**Advantages:**
- Zero code change.

**Disadvantages:**
- Officially abandons the mid/high-end market instead of fixing the model;
- The strongest signal in the engine stays deliberately unused; every
  above-ceiling domain still gets an identical recommendation.

**Cost Implications:** none.

**Risk Assessment:** High product risk — competitors (Estibot, NameBio
appraisal) answer the entire range; a tool that flatlines at €500 is
uncompetitive in its own market.

## Decision

**Chosen option: Option A — evidence-anchored value scale.**

The value scale must be *derived from evidence when evidence exists*, and
must remain byte-identical when it does not (zero-cost parity, community
edition). The comparable-sales median is the best evidence the engine
collects; deflating it through a €500 ceiling converts intelligence into
noise for every candidate above the ceiling. Option B adds a calibrated curve
that duplicates what the median already says, at tuning cost (ADR-0008) with
no correctness benefit. Option C keeps the product a scalper tool and wastes
the market signal — rejected on decision utility.

The ceiling itself is demoted from a model constant to an operator preference:
`BUY_MAX_ABSOLUTE_CAP` default 0 = uncapped, positive = hard per-deal budget.
Conservatism is preserved through the unchanged gate stack: market-score
saturation at 1.0, the density adjustment scaling sparse comparables, the
confidence derate (`conservative = aggressive × confidence`, cap 0.8), and
`buyMaxRatio = 0.5`.

## Consequences

### Positive
- The engine now distinguishes a €300 closeout flip from a €50k aftermarket
  domain; value tracks evidence.
- Hardened checkpoints (format version + 24h staleness) and the tenant-scoped
  heartbeat ship in the same branch, removing two production traps.
- No backtest re-tune required: `recommended`, weights, thresholds untouched.

### Negative
- Monetary figures change for market-data runs — consumers of
  `expectedValue`/`suggestedListPrice` (dashboard, CLI, portfolio NPV) will
  see numerically larger values.
- A single outlier comparable can lift the anchor — mitigated as documented,
  not eliminated.

### Compliance and Security Implications
- None. No new data flows; no privileged paths.

### Migration and Monitoring Plan
- Defaults change in code; existing deployments that set
  `BUY_MAX_ABSOLUTE_CAP=500` keep the old ceiling (no behavioural break).
- Metrics to watch: distribution of `suggestedBuyMax` across runs (expect a
  long tail above €250 that previously did not exist), and the backtest
  accuracy series (ADR-0008) — it should be unchanged, by design.
- Rollback: set `BUY_MAX_ABSOLUTE_CAP=500` — restores the historical cap.

### Validation
- Unit tests: anchoring with a €200k median yields `expectedValue > 500` and
  `suggestedBuyMax ≤ expectedValue × 0.5`; no-market runs stay ≤ 500.
- Backtest suite runs green on the weight-tuning loop.
- Timeline: 1 release cycle before judging the value distribution.

---

*This ADR was created following the MADR 4.0.0 standard. All DOMINUS ADRs should be consistent with the ADR series starting at `docs/adr/0001-project-architecture.md`.*