Status: descriptive contract, read from the code at repository revision a805a6bd
(2026-10-14). It records what each production metric actually computes today, not
what a theory document says it should compute. Where the two differ, the code wins and
the gap is stated as an open item.
This page implements the “Requirements” clause of issue #86 (refs #86): a metric or mechanism that feeds a recommendation must document its assumptions, observables, uncertainty, prediction, baseline, failure modes (including Goodhart risks), and human and security constraints. It does not close #86: it documents the current state and makes the gaps visible and guarded; it does not remove them.
Companion pages: Metric Uncertainty v0.1 (the Beta-Binomial helper and reporting rules) and Collaboration Observables v0.1 (the collaboration analyzer and its preregistered analysis). Those pages describe the shared machinery; this page is the per-metric contract.
tests/test_metric_contracts.py reads the metric keys out of the code by AST and fails if
a key has no ### entry here, if an entry lacks any of the seven labelled fields, or if an
entry names a metric the code no longer emits. It checks that the fields exist and are
non-empty. It cannot check that the prose is true; that remains a review duty.
These apply to many entries and are stated once so each entry can stay short.
config/evolution-policy-v1.json
holds uniform replicator priors (six at 0.142857, one at 0.142858), the carrying-capacity
constants (K, tau, issue and PR weights), the targets, and the control-energy weights.
state/evolution-live-policy.json holds a second hand-authored set for the live governor.
Nothing in the scoring path fits or updates them from outcomes. This is a known open
item, not a solved one.main_protection or branch_pressure is a deterministic function of one snapshot. It has no
sampling interval, no posterior and no censoring model. Only the collaboration
Beta-Binomial rows and the bootstrap latency rows carry an interval, and those intervals
cover sampling noise under stated assumptions, not measurement validity.authority blocks set
recommendation_only, automatic_merge, automatic_branch_mutation and
automatic_issue_creation as shown in the code; no metric grants merge, moderation,
ranking, spending or policy authority.main_protection fails closed (absent means
0.0); workflow_pin_ratio fails open (absent means 1.0); the external-witness and
branch inputs default to 0. Each entry says which applies.Which module emits which metric. scripts/evolution_live_governor.py duplicates part of
scripts/repository_evolution_score.py rather than importing it, so the two must be read
separately. As of the audited revision the governor is referenced by its own test and the
operations module map, and by no workflow under .github/workflows/.
| Metric | Emitted by | Function |
|---|---|---|
main_protection |
score, governor | normalized_signals / live_signals |
review_capacity |
score, governor | capacity_metrics then normalized_signals / live_signals |
independent_review_coverage |
score, governor | review_metrics then normalized_signals / live_signals |
starter_task_supply |
score, governor | normalized_signals / live_signals |
external_witness |
score, governor | normalized_signals / live_signals |
workflow_pin_ratio |
score, governor | normalized_signals / live_signals |
work_diversity |
score (inside signals); governor (top-level field, outside signals) |
normalized_signals with shannon_diversity (score); work_diversity (governor) |
branch_pressure |
score only | normalized_signals |
branch_health |
governor only (1 - branch_pressure) |
live_signals |
control_energy_proxy |
score only | control_energy |
homeostatic_potential |
governor only | evolution_math.homeostatic_potential |
strategy_weights |
score only | strategy_pressures, replicator_response |
priority |
score only | _priority, _scored |
first_independent_review_latency, cycle_latency, review_concentration, ownership_concentration, contributor_recurrence, ci_evidence, review_queue, structural_debt, evidence_derived_strategy_priors |
scripts/collaboration_observables.py |
analyze |
The collaboration analyzer consumes a snapshot built by scripts/collaboration_snapshot.py;
the evolution score consumes a different snapshot built by scripts/evolution_snapshot.py.
The two snapshots do not share collection code, so a notion such as “independent review” is
implemented twice with different rules (stated in the relevant entries).
Source of inputs: scripts/evolution_snapshot.py (collect). All signals below are in
[0, 1] and are recomputed from scratch for each snapshot.
main_protectionprotected boolean for the default branch is an adequate proxy for “integration is externally protected”. Treats protection as present or absent, with no notion of how strong the rules are.integration.main_protected in the evolution snapshot, taken from the branch API protected field in evolution_snapshot.collect. normalized_signals maps true to 1.0 and everything else, including a missing key, to 0.0.1.0 forces mode GUARD in determine_mode, adds the main_unprotected blocker, drives protect strategy pressure and the protection_deficit weight (2.0), and emits the protect-main action marked requires_admin. No claim is made that protection predicts any outcome.1.0; there is no historical or peer baseline.1.0 does not mean the review gate is enforced. Fails closed on a missing key, which is the safe direction. Because it is a flag, the cheapest way to raise it is to enable a trivial rule.protect-main action needs a human with admin rights. Do not treat 1.0 as evidence of review independence.review_capacityK = 8.0 and tau = 2.0 are authored constants.capacity_metrics counts non-draft and draft open PRs, issues carrying the exact label growth-seed, and other open issues (capped at other_issue_cap = 20). Load is ready_pr_weight*ready + draft_pr_weight*draft + growth_seed_weight*growth + other_issue_weight*capped_other; capacity is 1 / (1 + exp((load - K) / tau))._request_all, at most five pages of 100) and truncation is not propagated into the value. The weights and logistic constants carry no interval.0.35, or with more ready PRs than max_ready_prs, determine_mode selects CONSOLIDATE. Below minimum_capacity (0.6) it adds the review_capacity_below_target blocker. It also feeds the consolidate, integrate and explore pressures and the capacity deficit. The code does not validate that this load model predicts actual review delay.max_ready_prs) coexists with it, and the review-queue age in the collaboration analyzer is the closest observed quantity. No comparison between them is made.growth-seed label lowers load without removing work. Label-driven weighting means whoever controls labels controls the value. The logistic saturates, so large changes in load near the extremes barely move capacity.independent_review_coverageAPPROVED or CHANGES_REQUESTED review on the PR’s current head SHA. Independence means only a different login, not a different organisation or an uncorrelated judgment.evolution_snapshot._normalize_pr records independent_review_count per open PR. review_metrics returns reviewed ready PRs divided by ready PRs, and 1.0 when there are no ready PRs. Reviews on an older head are not counted, and a PR whose review list was truncated counts as having none.review_metrics also emits review_coverage_evidence and approval_coverage_evidence (beta_binomial_summary of ready PRs with an independent review or approval), so 1/1 is distinguishable from 100/100 and the empty case reports prior_only_no_observations. No decision rule reads the evidence blocks yet; they are advisory (see METRIC_UNCERTAINTY_V0_1.md).0.5 with at least one ready PR selects mode VERIFY; below 1.0 adds the ready_prs_lack_independent_review blocker; it drives verify and integrate pressure and the review deficit (weight 1.2). Whether coverage predicts defect escape or recurrence is not tested here.1.0.1.0, so a repository with no ready PRs looks perfectly covered. Rubber-stamp approvals and a second account controlled by the author both count as independent. A force-push after approval resets coverage to zero for that PR, which is conservative but noisy. The count is of reviewers, not of review quality.starter_task_supplygood first issue measures how much bounded newcomer work is available, and two such issues is enough (minimum_starter_tasks = 2).good first issue, divided by max(1, minimum_starter_tasks) and clamped to [0, 1] in normalized_signals.1.0 while review_capacity >= 0.5 emits the improve-starter-surface action, and it feeds the onboard pressure and the starter deficit (weight 0.8). The link from starter-task supply to contributor arrival is a hypothesis and is not validated here.external_witnessminimum_external_participants = 1).external_participant_count in the evolution snapshot: distinct logins, other than the repository owner and accounts typed as bots, seen as authors of open issues and PRs, authors of PRs merged in the last 30 days, reviewers, and commenters. normalized_signals divides by max(1, minimum_external_participants) and clamps.0.1.0 together with low starter supply and capacity at or above 0.5 selects ONBOARD; it feeds the onboard pressure and the external-witness deficit (weight 1.2). No outcome is predicted from it.workflow_pin_ratiouses: reference pinned to a 40-hex commit SHA is supply-chain-safer than a floating tag or branch, and every external reference deserves equal weight.evolution_snapshot.scan_workflow_pins walks .github/workflows/*.y*ml line by line with a regular expression (not a YAML parser), skips ./ and docker:// references and references without @, and reports pinned over total, or 1.0 when there are none. normalized_signals clamps it.minimum_workflow_pin_ratio (0.95) adds the workflow_dependencies_not_fully_pinned blocker, emits pin-workflow-dependencies, and feeds the maintain pressure and the pin deficit (weight 0.8). It is a hygiene rule, not a forecast of compromise.workflow_supply_chain or pin_ratio is absent the value is 1.0. A SHA pin proves immutability, not safety, and a pin to a malicious or abandoned commit scores perfectly. Every use counts equally, so a privileged action and a trivial one weigh the same. Pinning can be satisfied while the real exposure lives in scripts or reusable workflows.work_diversityCATEGORY_KEYWORDS (verification, community, research, maintenance, governance, product, other) partitions open work meaningfully, and a more even spread over the observed categories is healthier.log(k) for the k categories actually present. Zero items or one category give 0.0. The score computes it in shannon_diversity; the governor computes the same normalised quantity via evolution_math.normalized_entropy and emits it outside signals.explore pressure; it is not in the control energy or in determine_mode. In the governor it is emitted but not fed to the homeostatic potential. No outcome is predicted from it.1.0 as seven evenly split ones, so there is no baseline for how many categories should exist.branch_pressurebranch_count (number of branches returned by the branches API, at most three pages of 100) in the evolution snapshot. normalized_signals computes clamp01(max(0, branches - soft) / soft) with soft = max(1, maximum_branch_count_soft) (40).0 for any count at or below the soft limit and saturates at 1.0 from twice the limit.consolidate and maintain pressures, the branch_pressure deficit (weight 0.4), and emits converge-stale-branches when positive. Whether branch count predicts coordination cost is not tested.40 is an authored constant.0, which hides growth until it crosses. Deleting branches to move the number is destructive and is exactly what the recommendation is not allowed to do automatically.branch_healthbranch_pressure; it is the governor’s complement of the same quantity.live_signals in scripts/evolution_live_governor.py computes 1.0 - branch_pressure from branch_count and maximum_branch_count_soft with the same formula. Present only in the governor, not in the score.1.0, weight 0.4 in state/evolution-live-policy.json) and triggers the repository_hygiene need when below 1.0. Not validated against any outcome.1.0.branch_pressure, with the additional risk that the two modules drift apart because the formula is duplicated rather than shared.branch_pressure.control_energy_proxycontrol_energy in scripts/repository_evolution_score.py: protection, review, starter, external-witness and workflow-pin deficits are 1 - signal; the capacity deficit is clamp01((minimum_capacity - capacity) / minimum_capacity); the branch term is the signal itself. Energy is sum(weight * deficit^2) with the weights from control_energy_weights.2.0, 1.4, 1.2, 0.8, 1.2, 0.8, 0.4) are hand-authored and carry no interval; error in the inputs is not propagated.determine_mode does not read it, so it does not currently change any decision. No claim is made that lower energy predicts a better outcome.work_diversity is not an input. Because the weights are authored, the ranking of what matters is an opinion presented as a number.homeostatic_potentialevolution_math.homeostatic_potential over the seven live_signals using homeostasis.targets, scales and weights from state/evolution-live-policy.json. The key sets must match exactly or it raises.1.0; review_capacity targets 0.75.control_energy_proxy; a second policy file means the two modules can disagree on what is healthy. Targets of 1.0 reward saturation.strategy_weightseta = 0.8 and a mutation rate mu = 0.08 turns the current strategy pressures into an allocation. The pressures themselves are hand-weighted linear formulas of the signals (constants such as 1.5, 0.55, 0.65 live in strategy_pressures, not in the policy).strategy_pressures maps signals and ready-PR count to seven non-negative pressures; replicator_response multiplies each prior by exp(eta * (pressure - mean_fitness)), renormalises, mixes in mu/n uniform mutation, and renormalises. The report calls it a response to the current evidence, not historical learning.eta drives the result.evidence_derived_strategy_priors, but the score does not consume them; the only consumer found at the audited revision is the issue evidence gate, which counts them. The weights read as a learned distribution but are an authored response. Mutation mixing guarantees every strategy keeps nonzero weight regardless of evidence.priority1 + review, complexity, coordination and risk. Each part is in [0, 1]._priority and _scored in scripts/repository_evolution_score.py. Each part carries a provenance label: snapshot_derived (only unlock, from the same-repository reference in-degree divided by five), snapshot_conditioned_prior (an authored constant selected by an observed boolean, such as whether a PR has an independent review), or hand_authored_prior.priority_bounds is a one-sided joint perturbation of every non-derived part by AUTHORED_SENSITIVITY = 0.25, in the helping and hurting direction. The emitted priority_uncertainty states bounds_are_a_confidence_interval: false; the fraction is itself authored. Adjacent recommendations whose bounds overlap are flagged by separated_from_next.priority may still be unseparated from its neighbour.requires_admin need a human. Nothing is executed or opened automatically.Source: scripts/collaboration_observables.py (analyze, method
collaboration-observables-v0.4), over a snapshot from scripts/collaboration_snapshot.py.
Logins are pseudonymised in that snapshot (actor: plus a repository-salted digest prefix).
The snapshot’s own limitations list states that the window is bounded and not full
history, that merged PRs are the only contribution proxy, and that ownership is the last
merged toucher inside the window.
first_independent_review_latencyCOMMENTED review counts if its body is non-empty, together with APPROVED and CHANGES_REQUESTED.review_ready_at is the first ready_for_review timeline event, else the creation time for a non-draft PR. first_independent_review_at is the earliest eligible review at or after that time and not after the cutoff. The analyzer computes hours for PRs that have both, plus counts of right-censored PRs (no review, still open) and closed_without_review.median_hours: null.scripts/prereg_first_review_latency.py, PREREG_FIRST_REVIEW_LATENCY_V1.md) contrasts first review at or below 72 hours with above 72 hours against 90-day recurrence. It is observational; a causal claim would need a separate randomised or quasi-experimental design.cycle_latencyclosed_at minus created_at for closed PRs, in hours; right_censored is the count of PRs with no closed_at.closed_at includes closed-without-merge, so a faster close is not a faster acceptance. Closing stale PRs en masse shortens it. Censoring and PR-size heterogeneity are not adjusted for.review_concentration_hhi over lower-cased reviewer logins, one entry per distinct reviewer per PR; sum of squared shares. The analyzer raises an error if a listed reviewer is the author or a bot. Reported with counts, the number of distinct actors and an explicit status.descriptive_snapshot_no_population_inference. With no pairs the index is null and the status is undefined_empty_population, never a default value.0.5.counts field holds pseudonymised actors; keep it minimised in any public artifact and do not use it to single out individuals.ownership_concentration_hhi over changed_file_owners, one attribution per changed path per PR; the snapshot builds them from OWNERSHIP_MODEL = last_merged_toucher_within_window-v1, and the collector does not read CODEOWNERS.review_concentration, null when there are no attributions.review_concentration.contributor_recurrenceanalyze counts contributors with two or more distinct meaningful-contribution timestamps over all contributors, and passes the counts to beta_binomial_summary. Every listed contributor has at least one contribution by construction, so the success rate is conditional on that.beta-binomial-v3) with exact equal-tail 95% credible interval (credible_interval_95) from the Beta quantile, the empirical rate kept separate from the posterior mean, and prior_only_no_observations when there are no contributors. The prior is the default Beta(1, 1), a hand-authored uniform prior; this is a known open item. The exchangeability assumption is almost certainly wrong for contributors (heavy-tailed activity) and the interval does not account for it.ci_evidencesuccess. Conclusive means success, failure, cancelled, timed_out, action_required, stale or startup_failure; neutral, skipped and in-progress runs are excluded._ci_counts per PR from the latest check runs on the head SHA; the analyzer sums passed and total across all PRs and calls beta_binomial_summary.beta-binomial-v3 exact equal-tail 95% interval and default Beta(1, 1) prior (hand-authored, known open item). Zero checks is reported as prior-only. The effective sample size is overstated because check runs on one PR are strongly dependent (matrix legs, retries, shared infrastructure failures) and pooling weights large PRs more.review_queuereview_ready true and no closure, age in hours from review_ready_at to the cutoff; the analyzer reports the count, the median and a linearly interpolated 95th percentile.point_in_time_queue-v1). With few PRs the 95th percentile is close to the maximum.review_capacity, but they are never compared.structural_debtanalyze counts distinct structural_debt_finding_ids across PRs and reports the IDs, the number of PRs observed, and the snapshot’s inventory_complete flag, which is true only if a report was supplied and every finding reached a PR.bounded_inventory_count-v1). It is a lower bound on debt when the inventory is incomplete.evidence_derived_strategy_priorsverified_useful with independent verification are exchangeable Bernoulli trials per strategy, and normalising the per-strategy posterior means gives a usable weight. The independence of verification is asserted by the snapshot, not checked.strategy need a boolean verified_useful and verification_independent true, otherwise the analyzer raises an error. Per strategy, beta_binomial_summary over verified-useful counts, then normalized_weight = posterior_mean / sum of posterior_means.beta-binomial-v3 summary with the exact equal-tail 95% interval, default Beta(1, 1) prior (hand-authored, known open item). The normalised weight itself carries no interval. With one strategy the weight is 1.0 regardless of sample size.repository_evolution_score.py consumes it; the issue evidence gate counts the rows.authority.policy_activation is false in the output.These outputs are emitted but not contracted here. They are derived from, or are rules over,
the metrics above: the mode and blockers threshold rules, control_deficits,
strategy_pressure, dependency_graph, review.approval_coverage, and the preregistered
first-review-latency analysis (which has its own preregistration). Adding any of them as a
standalone metric would add an entry here and a row to the guard test.