# v8 ForkScore — Phase 1a reference

Reference for `arborist.substrate.fork_score` (ticket #000012 Phase 1a;
module moved from `arborist.v8.fork_score` to `arborist.substrate.fork_score`
on 2026-05-10 when the version-prefixed namespace pattern was retired —
the `v` in `v8` referred to the substrate-paper version, which collided
with the `v9.8` SQLite schema version and confused readers).
**Pure scoring function** over a (parent, child) BatteryResult pair.
No validator state machine, no consensus protocol, no acceptance
ledger — those are commissioned by the v8 paper itself, still open
under #000012.

This document is the **canonical source** for the formula, term
semantics, and verdict thresholds. Tests in
`tests/test_v8_fork_score.py` enforce them.

## 1. Formula

```text
ForkScore =
    α · Δ5S
  + β · Δ5T
  + γ · Δ5F                                  # incl. efficiency-aware bonus
  + δ · SelfModelCalibrationGain
  + ε · AuditCompleteness
  + ζ · ValidatorDiversity
  - η · RegressionPenalty
  - θ · CapitalCostPenalty
  - ι · SecurityRiskPenalty                  # reserved (Phase 1a = 0)
  - κ · ComplexityPenalty                    # reserved (Phase 1a = 0)
  - λ · MemoryInvalidationPenalty
```

Sign convention: weights are non-negative; the sign of each term is
encoded in the formula. `breakdown` keys in the output reflect the
sign:

```json
{
  "alpha_x_delta_5s": 0.025,
  "minus_eta_x_regression_penalty": -0.0,
  "minus_lambda_x_memory_invalidation": -0.5,
  ...
}
```

## 2. Term semantics

### 2.1 Δ5S, Δ5T, Δ5F (Δ-rate over each battery)

**Δ5S** = mean of per-sub-battery rate deltas across the 5S battery:

```
syntax           parse_pass_rate
semantics        equivalence_recovery_rate
syllogism        step_validity_rate
synthesis        derivation_pass_rate
semiotics        invariance_under_swap
```

**Δ5T** = same shape, **canonical Dav1DPrometheus vocabulary** (the
legacy `transfer` sub-battery is intentionally excluded so v2
fixtures drive the score):

```
transfer-learning   transfer_learning_success_rate
triangulation       triangulation_agreement_rate
truthtables         truth_table_coverage_rate
transitivity        full_chain_pass_rate
time                temporal_context_preservation_rate
```

**Δ5F** = mean of per-sub-battery rate deltas **plus** an
efficiency-aware bonus:

```
function       function_pass_rate
finetuning     adaptation_improvement_rate
falsification  error_detection_rate
formulate      structural_match_rate
feedback-loop  integration_coverage_rate
```

The efficiency bonus is computed from the metrics shipped under
ticket #000025's zero-cost guards:

- `adaptation_efficiency_mean_finite` — Δ in mean efficiency,
  damped by 0.1 to keep efficiency from dominating rate-style
  improvements.
- `adaptation_efficiency_infinite_count` — increase = bonus capped
  at `INFINITE_BONUS_CAP` (default 1.0). +∞ buckets must not pollute
  the ranking.
- `adaptation_efficiency_neg_infinite_count` — increase = `NEG_INF_REGRESSION`
  flag. **Hard reject** (a free regression is unsafe).

Same shape applies to `feedback_efficiency_*` keys.

### 2.2 SelfModelCalibrationGain

Distance closed between SelfModel capability claim threshold and
measured value. Phase 1a callers pass this as a scalar (default 0).
Phase 1b will compute it from `selfmodel_capability_claims` history.

### 2.3 AuditCompleteness

Fraction of state-changing ops with an `audit_event` row. Phase 1a
caller-supplied; Phase 1b reads from the chain directly. Range
`[0, 1]`; missing rows visible in the audit chain bring this below 1.

### 2.4 ValidatorDiversity

Multi-validator agreement entropy. **Zero in single-validator
mode** (Phase 1a default). v8 paper defines the multi-validator
calibration. With ζ = 0 by default, this term has no effect on
single-validator deployments.

### 2.5 RegressionPenalty

Sum over batteries of `max(0, -Δ_battery)`. A battery whose mean
delta is negative contributes its absolute value to the penalty
term (multiplied by η).

This is **softer** than the hard-regression flag (§3.2): if 4 of 5
sub-batteries improve by 10pp and 1 drops by 8pp, the mean Δ is
positive and `RegressionPenalty = 0` — but the hard flag still
fires on that one sub-battery.

### 2.6 CapitalCostPenalty

Capital cost delta from the #000020 ledger. Phase 1a accepts a
scalar `capital_delta`; positive means the child costs more. Phase
1b will compute it from `capital_ledger.summary`.

### 2.7 SecurityRiskPenalty, ComplexityPenalty

**Reserved for Phase 1b+.** Defaults `ι = κ = 0`. No security-bench
fixtures or LOC-delta surface exist in Phase 1a.

### 2.8 MemoryInvalidationPenalty

Count of `memory_records` rows the child fork would falsify. Phase
1a accepts a scalar; Phase 1b will compute it by simulating the
fork's verifier-method-root delta and counting memory_records that
cite the changed verifier.

## 3. Verdicts

### 3.1 Verdict thresholds

| Score | Hard flags | Verdict |
|---|---|---|
| ≥ `SIGNAL_FLOOR` (0.05) | none | **ACCEPT** |
| `[0, SIGNAL_FLOOR)` | none | **MARGINAL** (insufficient signal) |
| `< 0` | any | **REJECT** |
| any | hard-regression OR neg-inf efficiency | **REJECT** |

CLI exit code: `0` for ACCEPT/MARGINAL, `1` for REJECT — so CI
gates can run `arborist substrate score …` directly.

### 3.2 Hard flags

- **REGRESSION_5S/5T/5F:** any single sub-battery dropping by ≥
  `HARD_REGRESSION_FLOOR` (default 5pp). Reported per sub-battery
  in `flags`.
- **NEG_INF_REGRESSION:** `*_efficiency_neg_infinite_count`
  increased parent → child. Free regression is unsafe regardless
  of other gains.

A hard flag forces REJECT even when the net score is positive.
Operators can disable hard-flag gating in Phase 1b by overriding
the `_delta_*` helpers — but the soft `RegressionPenalty` term
stays in the formula.

## 4. Default weights

```python
WeightSet(
    alpha=1.0,    # Δ5S
    beta=1.0,     # Δ5T
    gamma=1.0,    # Δ5F
    delta=0.5,    # SelfModelCalibrationGain
    epsilon=0.3,  # AuditCompleteness
    zeta=0.0,     # ValidatorDiversity (off in single-validator)
    eta=2.0,      # RegressionPenalty (heavy by design)
    theta=0.5,    # CapitalCostPenalty
    iota=0.0,     # SecurityRiskPenalty (reserved)
    kappa=0.0,    # ComplexityPenalty (reserved)
    lambda_=0.5,  # MemoryInvalidationPenalty
)
```

Override via JSON file passed to `--weights`:

```json
{
  "alpha": 1.5,
  "eta": 5.0,
  "lambda": 1.0
}
```

(The JSON key `"lambda"` round-trips into `WeightSet.lambda_`
because `lambda` is a Python reserved word.)

## 5. CLI

```bash
arborist substrate score \
  --parent  parent-bench.json \
  --child   child-bench.json \
  [--weights weights.json] \
  [--capital-delta N] \
  [--selfmodel-calibration-gain N] \
  [--audit-completeness N] \
  [--memory-invalidation-count N]
```

`parent-bench.json` and `child-bench.json` are the JSON output of
`bench.batteries.runner --all` for each organism. The CLI prints
the `ScoredFork` as JSON and exits 0 on ACCEPT/MARGINAL, 1 on
REJECT.

## 6. What's NOT in Phase 1a

- Validator state machine (bonding / signing / slashing).
- Acceptance protocol (proposal / quorum / finalization).
- Challenge protocol (audit-replay disagreement).
- Fork-choice rule (which of two competing finalizations wins).
- Mesh wire format extensions for validator gossip.
- Stake mechanics + economic incentives.
- Cross-validator ZK proof exchange.

These are commissioned by the **v8 paper itself** — `docs/merkle-agi-v8-consensus.rst`, still open under ticket #000012. Phase 1a's
scoring function is the substrate the v8 paper consumes; landing
it now lets the paper cite measured values instead of stipulated
ones.

## 7. Closure of the gap fox identified

The 2026-05-08 review of `fbd99a8` flagged: *"the immediate
frontier has shifted from 'build the fitness surface' to 'define
how an organism mutation becomes canonical under that fitness
surface.'"*

Phase 1a closes the **scoring** half of that frontier. The
**canonicalization** half (validator agreement, fork choice,
slashing) remains under #000012 as the v8 paper deliverable.
