# 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.