COMPO · 2.20 · LINE-SYSTEM SCORER

Скоринг
линейной
системы.

Локальный scorer линейной подсистемы: сравнивает candidates из Compo 2.19 по owner-результатам Compo 2.02–2.18, не пересчитывает чужие метрики, не подменяет N/A нулём, подавляет повторные голоса по общей evidence lineage и возвращает воспроизводимый локальный ranking. Global composition остаётся за будущими слоями.

ModuleCompo 2.20
Scopelocal line-system scoring + ranking
OwnslineSystem.local_score + confidence + rank key + suppression log
Consumes2.19 candidates + owner MetricResult / PenaltyResult
ProvidesLineSystemScoreResult + diagnostics + correction routing
FinalityLOCAL LINE SYSTEM RANKING ONLY

Контракт scorer

00 / module api

Задача 2.20 — превратить набор уже измеренных line candidates в локальный, объяснимый и воспроизводимый порядок. Scorer не является ни geometry-owner, ни generator, ни global composition solver.

SCOPE

Local scoring / ranking линейной подсистемы при одном explicit semantic + style target.

BOUNDARY

Никакой прямой мутации path, stroke, color, grid или foreign-owned state.

DEFERRED

Global balance, hierarchy, negative space, global eye-flow, typography, image/crop.

CONSUMES / PROVIDES

Consumes
LineStructureCandidate[]              // from 2.19
MetricResult[]                    // owner results 2.02–2.18
PenaltyResult[]?
Candidate.hardViolations[]
Candidate.auditEvidence[]
Candidate.correctionCost?
Candidate.lineage
LineSystemTarget                  // semantic + style
LineScoringPolicy
Compo0.seed
Provides
LineSystemScoreResult {
  status,
  rankEligible,
  localScore,
  rawPositiveScore,
  softPenalty,
  confidence,
  evidenceCoverage,
  targetCoverage,
  groupBreakdown[],
  selectedVotes[],
  suppressedVotes[],
  appliedPenalties[],
  suppressedPenalties[],
  rejectedBy[],
  unresolvedInputs[],
  repairHints[],
  dirtyOwners[],
  comparisonKey,
  audit
}
NO FOREIGN MUTATION. 2.20 не двигает endpoints/control points/vertices/anchors, не меняет stroke width, color, opacity, dash pattern и не перестраивает grid. Исправление идёт только через owner-issued correction request обратно в bounded corrective loop 2.19.

Ownership: что 2.20 имеет право считать

01 / canonical owners

Scorer владеет только агрегатом и аудитом процесса агрегации. Исходная геометрия и локальные fit/penalty остаются у владельцев.

OWNS

Итог локальной системы

lineSystem.local_score
lineSystem.local_score_confidence
lineSystem.local_rank_key
lineSystem.suppression_log

CONSUMES

Owner results

Только нормализованные результаты и penalties, созданные Compo 2.02–2.18. 2.01 остаётся геометрическим descriptor-owner и сам по себе не получает эстетического голоса.

DOES NOT OWN

Global composition

global.balance, global.hierarchy, global.negativeSpace, global.eyeFlow, typography, image crop, poster score, semantic truth.

Главное правило

ONE METRIC = ONE OWNER measure/normalize → owner module aggregate/rank → Compo 2.20 mutate/repair → owning module or Compo 2.19 orchestration global override → future global/context layer (2.23 + global topics)

Никакого beautyScore. Локальный scalar нужен только для выбора между line candidates в одинаковом explicit target context. Он не утверждает, что один постер «красивее» другого.

Canonical owner inputs

ИсточникOwner resultИспользование в 2.20
Compo 2.02–2.05orientation / curve / polyline owner-fit, если owner его экспортируетDescriptors сами по себе не голосуют; voting разрешён только нормализованному owner-result.
Compo 2.06local visual-weight fitness/compositePrimary vote; raw stroke width и reused contrast остаются evidence.
Compo 2.07line.roleLengthFitPrimary ROLE_LENGTH vote; raw path length/span — diagnostics.
Compo 2.08local eye-guide cue resultТолько local cue; global eye-flow запрещён.
Compo 2.09semantic connector / route compositeТолько если connector/annotation semantics применимы.
Compo 2.10intersection owner fitness / penalty / hard violationОдин crossing-defect получает один owner vote или penalty.
Compo 2.11line-shape contact owner compositeValid attachment не дублируется generic contact penalty.
Compo 2.12parallel-group owner compositeGeometry gaps — evidence; stroke rhythm принадлежит 2.17.
Compo 2.13line.grid.localGridFitPrimary grid-related vote; axis/offset/endpoint components — evidence.
Compo 2.14line.boundary.localBoundaryScorePrimary boundary vote; component area ratios не превращаются в balance.
Compo 2.15–2.18owner-designated composite via scoringGroup2.20 не угадывает внутренний key; использует metadata contract.

Composite-first policy: если owner уже экспортирует локальный composite, scorer использует его как основной голос, а дочерние компоненты сохраняет в diagnostics. Это канонический механизм защиты от double counting.

Explicit semantic + style targets

02 / explicit target profile

Одна и та же геометрия может быть хорошим connector и плохим divider. Поэтому score не существует без явного target profile.

Semantic target

semanticTarget: {
  role: "connector",
  subrole: "annotation" | null,
  requiredGroups: ["ROLE_LENGTH","CONNECTION"],
  optionalGroups: ["FLOW_CUE","GRID_RELATION"],
  forbiddenRelations: ["UNEXPLAINED_PENETRATION"],
  requiredEvidencePolicy: "rank_with_low_confidence"
}

Style target

styleTarget: {
  presetId: "technical-clean-v1",
  groupMultipliers: {
    GRID_RELATION: 1.15,
    STROKE_PATTERN: 1.10,
    COLOR_TONE: 0.90
  },
  metricMultipliers: {},
  strictness: 0.70
}
Target не переписывает измерение. Style/semantic profile может менять applicability и weight, но не имеет права изменять MetricResult.value. Например, line.grid.localGridFit=.94 остаётся .94; если сетка не нужна, её applicability становится 0 или группа не включается в target.

Canonical MetricResult + PenaltyResult

03 / normalized interface

2.20 принимает только owner-нормализованные результаты. Dependency metadata обязательна: без неё нельзя доказуемо различить независимый сигнал и повторный голос по той же evidence lineage.

MetricResult

MetricResult {
  key: string,
  owner: "Compo2.xx",
  value: float,                  // 0..1; higher = better fit to owner target
  applicability: float,          // 0..1
  confidence: float,             // 0..1
  status: "ok|warn|fail|na",
  provenance:
    "DERIVED|STANDARD|EMPIRICAL|INITIAL_HEURISTIC|
     IMPLEMENTATION_HEURISTIC|STYLE_PRESET|DEFERRED",

  composite: bool,
  componentOf: string|null,
  dependsOn: string[],
  derivedFrom: string[],
  scoringGroup: string|null,
  independentVoteAllowed: bool,

  votePriority: number?,
  evidence: object,
  repairHints: []
}

PenaltyResult

PenaltyResult {
  key: string,
  owner: "Compo2.xx",
  severity: float,               // 0..1
  applicability: float,
  confidence: float,
  status: "ok|warn|fail|na",
  provenance: string,

  componentOf: string|null,
  dependsOn: string[],
  derivedFrom: string[],
  scoringGroup: string|null,
  independentVoteAllowed: bool,

  evidence: object,
  repairHints: []
}

Metadata semantics

FieldMeaningScoring consequence
dependsOnВычислительная зависимость / recompute order.Само по себе НЕ подавляет голос.
derivedFromРезультат реально использует upstream evidence.Проверяется на double counting внутри и между scoringGroups.
componentOfМетрика уже включена в active composite.Компонент становится diagnostic-only.
scoringGroupБюджет семейства голосов.Группа получает ограниченный общий influence.
independentVoteAllowedЯвное разрешение независимо голосовать при общей evidence lineage.Без явного разрешения повторный derived signal подавляется.

Validation / legacy adapter

if status == "na" → applicability = 0 if value ∉ [0,1] → invalid payload; diagnostic-only if confidence == 0 → no voting weight if owner missing → invalid payload if scoringGroup missing → diagnostic-only unless versioned legacy adapter maps owner if independentVoteAllowed missing: owners 2.02–2.14 → versioned bridge may inject group + independent=false ONLY for allowlisted primary keys owners 2.15–2.18 → diagnostic-only until metadata is explicit if normalization is needed → reject voting payload: normalization belongs to OWNER
DERIVEDСледует из канонической геометрии / отношений.
EMPIRICALКалибруется на корпусе / данных.
INITIAL_HEURISTICСтартовое рабочее число, не закон композиции.
STYLE_PRESETОсознанная настройка target/style.

Hard reject раньше score

04 / feasibility gate

Невыполнимый candidate не может компенсировать жёсткое нарушение хорошими локальными метриками.

FEASIBILITY GATE

if Candidate.hardViolations.length > 0: return { status: "rejected", rankEligible: false, localScore: null, confidence: 1, rejectedBy: Candidate.hardViolations }
Hard violation

Не переводится в soft penalty и не может быть «откуплен» высоким grid-fit, хорошим цветом или удачной кривизной.

Soft issue

Допускается только как owner-issued PenaltyResult с evidence, applicability и confidence; тот же дефект нельзя одновременно учитывать внутри composite и отдельным penalty.

Scoring groups: бюджет доказательств

05 / dynamic group weighting

Количество метрик в модуле не должно автоматически давать ему больше влияния. Голосовой бюджет назначается группе, а не числу полей.

GroupТипичные ownersКогда голосуетПримечание
AXIS_DIRECTION2.02 / 2.03axis/diagonal role-fit применимH/V и diagonal ветки взаимно исключаются через applicability.
PATH_FORM2.04 / 2.05curve или polylineЧистые descriptors arc length / turn angles не голосуют без owner fit.
VISUAL_WEIGHT2.06line renderedИспользовать owner composite/fitness; не пересчитывать contrast.
ROLE_LENGTH2.07target interval/reference span известенroleLengthFit — primary; raw path length — evidence.
FLOW_CUE2.08target запрашивает local guide cueЭто не global eye-flow.
CONNECTION2.09connector/annotation semanticsOwner composite semantic attachment/route fit.
INTERSECTION_SAFETY2.10есть line-line relationsЧаще owner penalty/fitness, а не «бонус за отсутствие линий».
SHAPE_CONTACT2.11есть object/shape relationValid attachment не должен повторно штрафоваться как generic touch.
PARALLEL_GROUP2.12реальная parallel group существуетGaps — geometry evidence; rhythm принадлежит 2.17.
GRID_RELATION2.13grid включён/запрошенN/A curve не получает 0.
BOUNDARY_PARTITION2.14divider/frame/boundary semanticsArea ratios не превращать в balance score.
DECORATIVE_TECH2.15owner declares applicability2.20 читает export, не угадывает имя метрики.
COLOR_TONE2.16rendered color/opacity availableНе дублировать contrast, если он уже вошёл в 2.06 contextual weight.
STROKE_PATTERN2.17dash/repeat pattern применимГеометрические gaps 2.12 — evidence, rhythm vote — 2.17.
BENDING_TENSION2.18curvature/tension target применимМожет зависеть от 2.04 geometry, но голосует только своим owner-fit.
Compatibility rule: для новых 2.15–2.18 2.20 не hardcode-ит внутренние ключи. Канонический путь — owner экспортирует scoringGroup, componentOf, dependsOn, derivedFrom и composite flag. Legacy bridge разрешён только как versioned allowlist для старых 2.02–2.14; неизвестный key остаётся diagnostic-only.

Anti double-counting: suppression policy

06 / evidence lineage

Double counting проверяется дважды: сначала внутри scoringGroup, затем между scoringGroups. Scorer не угадывает корреляцию по названиям — он работает только с owner metadata и evidence lineage.

Suppression order

1. N/A / applicability = 0
2. confidence = 0
3. invalid / incomplete metadata
4. componentOf active composite
5. duplicate owner+key+version
6. same scoringGroup → primary vote
   additional sibling only if independentVoteAllowed
7. cross-group derivedFrom / evidence overlap
   → suppress lower-priority vote unless BOTH explicitly allow independence
8. dependsOn alone → NEVER suppress
9. target says group not applicable
10. global / deferred metric → ignored by 2.20

SuppressedVote

{
  key,
  owner,
  reason:
    "COMPONENT_OF_COMPOSITE" |
    "GROUP_PRIMARY_SELECTED" |
    "CROSS_GROUP_DERIVED_DUPLICATE" |
    "DUPLICATE_EVIDENCE" |
    "ZERO_APPLICABILITY" |
    "ZERO_CONFIDENCE" |
    "INCOMPLETE_METADATA" |
    "TARGET_NOT_APPLICABLE" |
    "GLOBAL_DEFERRED",
  suppressedBy?: string,
  evidenceRef?: string
}

Cross-group rule

overlap(A,B) = A.derivedFrom contains B.key OR B.derivedFrom contains A.key OR intersect(A.derivedFrom, B.derivedFrom) != ∅ OR same explicit evidenceFingerprint if overlap(A,B): if A.independentVoteAllowed == true AND B.independentVoteAllowed == true: both may vote, each still bounded by its scoringGroup budget else: keep deterministic primary by composite → votePriority → confidence → owner/key suppress the other and log why
dependsOn ≠ derivedFrom.

Вычислительная зависимость нужна для dirty/recompute graph. Она не доказывает, что две метрики измеряют один и тот же факт.

Недостаточная metadata.

Metric становится diagnostic-only. COMPO предпочитает потерять голос, а не незаметно посчитать один evidence дважды.

Агрегация: group-capped weighted vote

07 / aggregation

Количество полей не создаёт дополнительного влияния. Сначала выбираются независимые owner votes, затем их влияние ограничивается бюджетом scoringGroup.

policyWeightᵢ = baseWeightᵢ × semanticMultiplierᵢ × styleMultiplierᵢ × applicabilityᵢ scoreWeightᵢ = policyWeightᵢ × confidenceᵢ for each scoringGroup g: selected_g = votes surviving all suppression passes rawGroupWeight = Σ scoreWeightᵢ groupWeight_g = min(rawGroupWeight, groupVoteCap_g) shareᵢ = scoreWeightᵢ / max(rawGroupWeight, EPS) groupValue_g = Σ shareᵢ × valueᵢ W = Σ groupWeight_g over active groups if W == 0 → status = "insufficient_metrics" positiveScore = Σ(groupWeight_g × groupValue_g) / W softPenalty = clamp(Σ deduplicatedOwnerPenaltyContribution, 0, maxSoftPenalty) localScore = clamp(positiveScore - softPenalty, 0, 1)
INITIAL_HEURISTIC

groupVoteCap

1.00 default group budget. Target может задать другой cap явно.

INITIAL_HEURISTIC

maxSoftPenalty

0.35. Hard violation никогда не конвертируется в soft penalty.

N/A

Не в знаменатель

Неприменимая target-group исключается. Отсутствие не превращается в value=0.

Composite-first

Один evidence

Один defect получает один primary positive/penalty representation, если независимость не разрешена явно.

Penalty de-duplication

Penalty проходит ту же lineage-проверку, что и positive vote. Одинаковый evidenceFingerprint не может быть списан дважды даже в разных scoringGroups. Если тот же evidence уже представлен active composite vote, penalty подавляется, пока owner явно не выставит independentVoteAllowed=true с обеих сторон.

Group cap ≠ universal law. Значение 1.00 — INITIAL_HEURISTIC для стартовой реализации. Его нужно калибровать и/или задавать через StylePreset, а не выдавать за закон композиции.

Score, confidence и coverage — разные величины

08 / epistemic quality

Score отвечает за соответствие target, confidence — за надёжность evidence, coverage — за полноту ожидаемых owner families. Они не смешиваются в один «магический» показатель.

Score

Насколько хорошо доступные применимые owner votes соответствуют explicit target.

Confidence

Насколько надёжны exact / approximate / fallback measurements и owner evaluation.

Coverage

Какая доля target-ожидаемых scoringGroups вообще присутствует как валидное evidence.

// confidence is NOT squared by reusing scoreWeight confidenceBaseWeightᵢ = baseWeightᵢ × semanticMultiplierᵢ × styleMultiplierᵢ × applicabilityᵢ groupConfidence_g = weightedMean(metric.confidence, weights=confidenceBaseWeightᵢ) sceneConfidence = weightedMean(groupConfidence_g, weights=groupWeight_g) expectedGroups = target.expectedGroups OR union(requiredGroups, optionalGroups) evidenceCoverage = presentExpectedGroups / expectedApplicableGroups targetCoverage = presentRequiredGroups / requiredGroups finalConfidence = sceneConfidence × evidenceCoverage

Missing required group не получает value=0. При requiredEvidencePolicy="require_all" candidate становится insufficient_evidence и не ранжируется. При rank_with_low_confidence он может остаться в ranking, но с пониженным coverage/confidence.

Exact / fallback.

Owner должен уже отразить качество geometry/evidence в confidence. 2.20 не пытается повторно угадать, была геометрия exact, approximate или fallback.

Deterministic ranking candidates из 2.19

09 / deterministic ranking

Одинаковые входы, seed, policy и target обязаны давать одинаковый порядок.

compare(A,B): 1. feasible / rankEligible beats rejected 2. if both rejected → stableTieBreak by candidateId|seed|lineageHash 3. if |A.localScore - B.localScore| > scoreEpsilon → higher score wins 4. if A.targetCoverage != B.targetCoverage → higher coverage wins 5. if |A.confidence - B.confidence| > confidenceEpsilon → higher confidence wins 6. if correctionCost differs → lower cost wins 7. stableTieBreak = hash(candidateId | seed | lineageHash) INITIAL_HEURISTIC: scoreEpsilon = 0.015 confidenceEpsilon = 0.030

Глобальный solver позже имеет право выбрать локально второй candidate, если он лучше работает с hierarchy, balance, negative space, typography, image interaction или global eye-flow. 2.20 не пытается предвосхитить этот слой.

Rank scope: сравнивать допустимо только candidates, созданные для одного semantic request / target profile. Нельзя напрямую сравнивать «идеальный divider» и «идеальный connector» одним локальным scalar без общей вышестоящей задачи.

Edge cases

CasePolicy
Все candidates rejectedScore не вычисляется; порядок для audit стабилен только по deterministic tie-break и не означает «лучший дизайн».
Нет ни одного валидного votestatus=insufficient_metrics, localScore=null, candidate не получает фиктивный ноль.
Target profile отсутствуетScorer не имеет права интерпретировать descriptors; допускается только diagnostic mode.
Owner metadata incompleteMetric diagnostic-only; запись в unresolvedInputs[].
Два разных semantic requestsИх localScore нельзя напрямую сравнивать как один общий scalar.

Explainability + corrective loop

10 / repair interface

Scorer должен возвращать причину проигрыша и владельца, которому разрешено исправление.

Contribution loss

contributionLoss_i =
  normalizedEffectiveWeight_i × (1 - value_i)

lowestPrimaryVotes =
  sort(selectedVotes by contributionLoss desc)
  take first 3

Это диагностический приоритет, а не новая метрика качества.

Repair merge

repairHints = mergeUnique(
  hardViolations.repairHints,
  lowestPrimaryVotes.repairHints,
  appliedPenalties.repairHints
)

dirtyOwners = unique(repairHints.targetOwner)

2.20 ничего не исправляет самостоятельно.

2.19 candidate → owner metrics 2.02–2.18 → 2.20 score + diagnostics → select lowest contribution / applied owner penalty → owner-issued correction request → 2.19 bounded regeneration using same lineage + deterministic seed fork → recompute dirty owners only → 2.20 rescore

Candidate generation boundary

2.20 НЕ генерирует candidates.

Candidate generation остаётся в 2.19: bounded, seeded, reproducible, с lineage, hard filtering, deduplication и diversity pruning. 2.20 может только вернуть correction requests и dirtyOwners для ограниченного regeneration pass.

Визуальная лаборатория

11 / visual reference

Четыре случая показывают ключевую идею scorer: geometry descriptor сам по себе не «хороший» и не «плохой»; решение появляется только относительно role/target и owner evidence.

A · CONNECTOR / TARGET FIT B · HARD CONFLICT / REJECT BEFORE SCORE C · N/A GRID / NO ZERO PENALTY grid.enabled = false D · SAME ANGLE DESCRIPTOR / DIFFERENT TARGET trajectory: fit can be highdivider: same θ, different fit
target fitsame descriptorN/A is excludedcontext/evidence
DESCRIPTOR != SCORE

Одинаковый угол ≠ одинаковое качество

Angle, curvature, span, stroke width и другие descriptors не получают самостоятельный эстетический vote. Owner интерпретирует их относительно role/target и экспортирует fit.

DOUBLE COUNTING

Suppressed не значит потерян

Подавленный signal остаётся в suppressedVotes[] вместе с owner, value, evidence и reason. Это часть audit trail.

LOCAL ONLY

Глобальный solver может переопределить

Локально второй line candidate может победить позже из-за hierarchy, balance, negative space, typography или image interaction.

Live mini-scorer

Target: connector / grid optional.

Детерминированные smoke tests

12 / inline JS tests

Это не замена 2.21. Здесь проверяются только invariants самого scorer: N/A, ownership, dependency metadata, suppression, coverage, penalties и deterministic ranking.

IDInvariantExpected
T01N/A не штрафуетmetric excluded from denominator
T02Hard rejectlocalScore=null; rankEligible=false
T03Composite-firstcomponent suppressed
T04Group capmetric count cannot inflate family weight
T05Cross-group derived overlaplower-priority duplicate suppressed
T06Explicit independent voteboth cross-group votes allowed only when both opt in
T07dependsOn onlyno suppression by dependency alone
T08Target applicabilitydisabled group excluded
T09No global leakageglobal.* cannot affect localScore
T10Confidence tie-breakwithin epsilon higher confidence wins
T11Deterministic feasible tierepeat run → same order
T12Deterministic rejected tieno undefined tie-break
T13Penalty cross-group dedupsame evidence applied once
T14Penalty vs primary vote dedupsame evidence not charged twice
T15Missing new-owner metadatadiagnostic-only + unresolvedInputs
T16Legacy adapter 2.13group + independent=false injected deterministically
T17Evidence coveragemissing expected group lowers confidence, not score via zero
T18Raw geometry descriptor2.01 angle/length cannot vote
T19Explicit target gatemissing target → diagnostic_only; localScore=null
Tests not run yet.

DATA FOR LAYOUT ENGINE

13 / machine contract

Machine-readable contract. Разработчик должен иметь возможность реализовать scorer почти без чтения пояснительного текста.

{
  "module": "Compo 2.20",
  "name": "line_system_scoring",
  "version": "1.2-corrective",
  "scope": "local scoring and deterministic ranking of Compo 2.19 line-system candidates under one explicit semantic/style target",
  "consumes": [
    "Compo2.19.LineStructureCandidate[]",
    "MetricResult[] from Compo2.02-Compo2.18",
    "PenaltyResult[] from metric owners",
    "Candidate.hardViolations[]",
    "Candidate.auditEvidence[]",
    "Candidate.correctionCost?",
    "Candidate.lineage",
    "LineSystemTarget.semanticTarget",
    "LineSystemTarget.styleTarget",
    "LineScoringPolicy",
    "Compo0.seed"
  ],
  "provides": [
    "LineSystemScoreResult",
    "lineSystem.local_score",
    "lineSystem.local_score_confidence",
    "lineSystem.local_rank_key",
    "lineSystem.suppression_log",
    "repairHints[]",
    "dirtyOwners[]"
  ],
  "owns_metrics": [
    "lineSystem.local_score",
    "lineSystem.local_score_confidence",
    "lineSystem.local_rank_key"
  ],
  "descriptors_only": [
    "suppression_log",
    "group_breakdown",
    "evidenceCoverage",
    "targetCoverage",
    "comparisonKey",
    "audit"
  ],
  "deferred_inputs": [
    "global.balance",
    "global.hierarchy",
    "global.negativeSpace",
    "global.eyeFlow",
    "typography.*",
    "imageCrop.*",
    "posterGlobalScore",
    "Compo2.23 context overrides"
  ],
  "hard_constraints": [
    "hard violation rejects before score",
    "foreign mutation forbidden",
    "raw geometry descriptors cannot vote",
    "normalization belongs to metric owner",
    "N/A excluded from vote and denominator",
    "global metrics cannot influence localScore"
  ],
  "soft_constraints": [
    "owner penalty requires evidence",
    "same evidence may not be penalized twice",
    "group influence is capped",
    "incomplete metadata becomes diagnostic-only"
  ],
  "preferences": [
    "composite-first",
    "explicit semantic/style target",
    "higher-confidence evidence has greater voting influence",
    "lower correction cost wins only after score/coverage/confidence tie rules"
  ],
  "overrides_allowed_by": [
    "Compo2.23 context override layer",
    "future global composition solver",
    "explicit StylePreset / semantic target for weights and applicability only"
  ],
  "initial_heuristics": {
    "groupVoteCap": 1.0,
    "maxSoftPenalty": 0.35,
    "scoreEpsilon": 0.015,
    "confidenceEpsilon": 0.03
  },
  "style_preset_values": {
    "groupVoteCaps": "optional explicit per-group",
    "semanticMultipliers": "optional explicit",
    "styleMultipliers": "optional explicit",
    "thresholds": "must carry STYLE_PRESET provenance"
  },
  "metric_policy": {
    "range": [
      0,
      1
    ],
    "higher_is_better": "owner target fit only, not universal beauty",
    "normalization_owner": "metric_owner",
    "composite_first": true,
    "exclude_na_from_denominator": true,
    "exclude_zero_confidence": true,
    "required_dependency_metadata": [
      "componentOf",
      "dependsOn",
      "derivedFrom",
      "scoringGroup",
      "independentVoteAllowed"
    ],
    "incomplete_metadata": "diagnostic_only",
    "double_counting": "check within scoringGroup and across scoringGroups"
  },
  "dependency_semantics": {
    "componentOf": "suppress component when parent composite votes",
    "derivedFrom": "lineage evidence used for duplicate suppression",
    "dependsOn": "recompute/order dependency only; does not suppress by itself",
    "scoringGroup": "vote-budget family",
    "independentVoteAllowed": "shared evidence may vote independently only when both overlapping metrics explicitly opt in"
  },
  "legacy_metadata_adapter": {
    "version": "line-owner-primary-bridge-v2",
    "scope": "Compo2.02-Compo2.14 only; explicit primary allowlist; unknown legacy owner metrics are diagnostic-only",
    "default_independentVoteAllowed": false,
    "new_owners_2_15_2_18": "must export explicit metadata"
  },
  "group_policy": {
    "groupWeight": "min(sum(scoreWeight), groupVoteCap)",
    "additional_same_group_vote": "requires explicit independentVoteAllowed on both representations; shared lineage is allowed only by explicit bilateral opt-in; group cap still bounds total influence",
    "group_cap_provenance": "INITIAL_HEURISTIC or STYLE_PRESET"
  },
  "aggregation": {
    "policyWeight": "baseWeight * semanticMultiplier * styleMultiplier * applicability",
    "scoreWeight": "policyWeight * confidence",
    "groupValue": "weightedMean(metric.value by scoreWeight)",
    "positiveScore": "weightedMean(groupValue by capped groupWeight)",
    "softPenalty": "clamp(sum(deduplicated owner penalty contributions),0,maxSoftPenalty)",
    "localScore": "clamp(positiveScore-softPenalty,0,1)"
  },
  "confidence": {
    "groupConfidence": "weightedMean(metric.confidence by policyWeight without confidence factor)",
    "sceneConfidence": "weightedMean(groupConfidence by active groupWeight)",
    "evidenceCoverage": "presentExpectedGroups / expectedApplicableGroups",
    "targetCoverage": "presentRequiredGroups / requiredGroups",
    "finalConfidence": "sceneConfidence * evidenceCoverage",
    "missing_required_group": "never inject value=0"
  },
  "penalty_policy": {
    "deduplicate_across_groups": true,
    "deduplicate_against_selected_votes": true,
    "independent_exception": "explicit independentVoteAllowed on both representations"
  },
  "hard_policy": {
    "any_owner_hard_violation": "REJECT",
    "hard_violation_compensable_by_score": false,
    "scorer_may_invent_hard_violation": false
  },
  "ranking": [
    "feasibility",
    "localScore outside scoreEpsilon",
    "targetCoverage",
    "confidence outside confidenceEpsilon",
    "lower correctionCost",
    "stable hash(candidateId|seed|lineageHash)"
  ],
  "correction_policy": {
    "direct_foreign_mutation": "FORBIDDEN",
    "repair_source": "owner-issued repairHints / correction requests",
    "orchestrator": "Compo 2.19",
    "recompute_when": "dirty owner dependency graph indicates upstream evidence changed",
    "recompute": "dirty owners only, then rescore"
  },
  "tests": [
    "T01_NA",
    "T02_HARD_REJECT",
    "T03_COMPOSITE",
    "T04_GROUP_CAP",
    "T05_CROSS_GROUP_DERIVED",
    "T06_INDEPENDENT_VOTE",
    "T07_DEPENDSON_ONLY",
    "T08_TARGET_APPLICABILITY",
    "T09_NO_GLOBAL_LEAKAGE",
    "T10_CONFIDENCE_TIE",
    "T11_DETERMINISTIC_TIE",
    "T12_REJECTED_TIE",
    "T13_PENALTY_DEDUP",
    "T14_PENALTY_VOTE_DEDUP",
    "T15_MISSING_METADATA",
    "T16_LEGACY_ADAPTER",
    "T17_EVIDENCE_COVERAGE",
    "T18_DESCRIPTOR_NOT_SCORE",
    "T19_EXPLICIT_TARGET_GATE"
  ],
  "recompute_when": [
    "candidate geometry/relations changed by owner correction",
    "semantic/style target changed",
    "owner MetricResult/PenaltyResult changed",
    "scoring policy or legacy adapter version changed"
  ],
  "finality": "LOCAL_LINE_SYSTEM_RANKING_ONLY",
  "invariants": [
    "ONE_METRIC_ONE_OWNER",
    "N_A_NOT_ZERO",
    "LOCAL_NOT_GLOBAL",
    "GEOMETRY_NOT_INTERPRETATION",
    "DESCRIPTOR_NOT_SCORE",
    "COMPOSITE_FIRST",
    "NO_BEAUTYSCORE",
    "NO_FOREIGN_MUTATION"
  ]
}
ARCHITECTURE CHECKPOINT · 2.20 владеет только локальным агрегатом / confidence / rank key. Геометрия остаётся у 2.01, owner fit / penalties — у 2.02–2.18, bounded generation — у 2.19, полный regression suite — у 2.21, mask/crop/seam — у 2.22, context/global overrides — у 2.23 и будущих global owners. Один evidence не получает второй голос без explicit independentVoteAllowed.
VISITS / ...