Тест — это executable contract
2.21 фиксирует наблюдаемое поведение публичных интерфейсов. Он не копирует production-логику owner-модулей и не превращает стартовые эвристики в вечные числовые истины.
Unit/reference/regression/integration invariants для line subsystem 2.00–2.23.
Ни одной новой line metric, никакого score ownership, никакой геометрической мутации.
Только будущие global composition owners: balance/hierarchy/negative-space/eye-flow, global pipeline/scoring и generic override system.
CONSUMES / PROVIDES
Compo 2.00–2.18 owner contracts Compo 2.19 LineStructureCandidate[] Compo 2.19 CandidateLineage / GenerationTrace Compo 2.20 LineSystemScoreResult Compo 2.22 MaskCropSeamResult Compo 2.23 LineContextProjection MetricResult[] / PenaltyResult[] StylePreset / semantic target seed / engineVersion / ownerVersion map
LineRegressionCase[] LineRegressionFixture[] LineRegressionSuite RegressionBaselineManifest SuiteRunReport FailureRecord[] CompatibilityMatrix deterministic smoke runner
line.grid.localGridFit, его expected owner остаётся Compo 2.13. 2.21 может проверить значение/status/owner, но не становится владельцем формулы.Типология тестов
Степень жёсткости зависит от природы утверждения. Чистая геометрия допускает exact/tight epsilon; target-fit и эвристические policy проверяются диапазоном или отношением кандидатов.
| Source class | Что фиксирует | Тип assertion | Пример |
|---|---|---|---|
| DERIVED | Чистая математика / геометрия / стандартная формула. | exact ± EPS | 3–4–5 segment → length=5. |
| CONSTRAINT | Hard boundary, ownership, forbidden mutation. | status / reject reason / boolean | hard violation нельзя компенсировать score. |
| HEURISTIC RANGE | Стартовая policy с provenance. | range / set membership | score drift в документированном допуске. |
| RELATIONAL | Сравнение вариантов при одном target. | A > B / same / excluded | confidence tie-break при score внутри epsilon. |
| DETERMINISM | Seeded generation / tie-break / lineage. | structural signature equality | same input + seed → same candidate signatures. |
| REGRESSION | Стабильность после corrective pass. | baseline diff + documented migration | rank inversion требует причины, а не переписывания expected. |
Допуски и provenance
Каждый допуск должен быть привязан к типу вычисления. Test suite не имеет права маскировать numerical instability широким «дизайнерским» tolerance.
Если baseline изменился из-за осознанной migration/corrective pass, expected обновляется только вместе с reason, ownerVersion и новой baseline version.
Coverage matrix 2.00–2.23
Каждый owner получает хотя бы один structural или numerical regression barrier. Composite modules 2.19/2.20 получают отдельные integration suites.
| Module | Область | Обязательные regression assertions | Representative IDs |
|---|---|---|---|
| 2.00 | roles / line schema | role/subrole contract; no style kind promoted to top-level role | O09 |
| 2.01 | base geometry | length, angle convention, reversal, degeneracy | G01–G07 |
| 2.02–2.03 | axis / diagonal | orientation modulo 180; direction-specific semantics explicit | G02–G04 |
| 2.04–2.05 | curve / polyline | arc/segment invariance; 2.04 owns raw smooth-curve curvature, 2.05 owns polyline turns | G05–G07, A09 |
| 2.06 | visual weight | owner remains local; reused contrast evidence not double-voted | O06, S09 |
| 2.07 | length ↔ role | descriptor ≠ fit; N/A target handling | O03, O13 |
| 2.08 | local eye-guide cue | no global eye-flow ownership/leakage | O08 |
| 2.09 | connectors | semantic endpoints / route relation remain owner evidence | G08, S04 |
| 2.10–2.11 | crossing / contact | hard/soft distinction; one defect one representation | G08, S01, S09 |
| 2.12 | parallel groups | geometry gaps ≠ rhythm score | O10 |
| 2.13 | grid | N/A ≠ 0; local grid fit only | O13, S02 |
| 2.14 | boundary/divider | partition descriptor cannot become global balance | O14 |
| 2.15 | technical/decorative grammar | graphicKind/subrole boundary | O09 |
| 2.16 | color/tone/opacity | linear-light compositing; raw contrast ≠ universal quality | A01–A03, O11 |
| 2.17 | rhythm / stroke pattern | dash canonicalization; phase wrap; model-fit not regularity worship | A04–A08, O10 |
| 2.18 | tension interpretation | consumes 2.04/2.05 raw evidence; owns derived energy/concentration + target-aware tension fit | A10, O12, O15 |
| 2.19 | generator | bounded + seeded + dedupe + lineage + immutable parent | GEN01–GEN06 |
| 2.20 | scorer | N/A, caps, coverage, suppression, deterministic ranking | S01–S10 |
| 2.21 | regression oracle | suite owns no design metrics; baselines versioned | R01–R02 |
| 2.22 | mask / crop / seam | topology, area conservation, reversal, explicit crop side, host adapter, no score ownership | M01–M06 |
| 2.23 | context adapter | immutable target projection, conflict defer, external selection trace, no local score rewrite | C01–C06 |
Geometry + invariance fixtures
Reference geometry проверяет не конкретную реализацию sampling, а наблюдаемые свойства: единицы, нормализацию, reversal, segmentation invariance и degeneracy.
Segment 3–4–5
A=(0,0) B=(3,4) expected: lengthPx = 5 finite = true
Same path / segmentation
A→D = 100 px A→B→C→D collinear expected: same pathLength same orientation no segment-count bias
Zero-length
A=(10,10) B=(10,10) expected: status=degenerate angle=N/A no NaN metric vote
Transformation invariants
Ownership, N/A и dependency barriers
Большая часть опасных regressions в COMPO — не числовая, а архитектурная: метрика начинает жить у двух owners, N/A превращается в ноль или descriptor внезапно начинает голосовать.
Owner registry assertions
line.rhythm.localRhythmScore → Compo2.17 line.color.localColorScore → Compo2.16 line.curvature.localTensionScore → Compo2.18 line.grid.localGridFit → Compo2.13 line.boundary.localBoundaryScore → Compo2.14 lineSystem.local_score → Compo2.20
Forbidden substitutions
N/A != 0 descriptor != score dependsOn != derivedFrom local cue != global eye-flow area ratio != global balance raw contrast != universal positive vote repair hint != foreign mutation
Double-counting barrier
Approximate/fallback measurement остаётся тем же owner-result, но с меньшим confidence. 2.21 проверяет, что fallback не маскируется как exact и не получает одинаковую epistemic weight.
Generator regression 2.19
2.19 тестируется как orchestration layer. Suite проверяет границы поиска и lineage, но не требует единственной формы connector/curve.
| ID | Input | Expected invariant |
|---|---|---|
| GEN01 | same request + seed + versions | same ordered structural signatures |
| GEN02 | maxTotalCandidates=5 | survivors ≤ 5 |
| GEN03 | one family produces hard-invalid branch | invalid branch absent from survivors, reason logged |
| GEN04 | duplicate structural signatures | one deterministic representative retained |
| GEN05 | owner correction on candidate | new child lineage; parent snapshot unchanged |
| GEN06 | many independent option arrays | bounded expansion; no full Cartesian product |
Scorer regression 2.20
2.21 закрепляет публичные invariants scorer, включая те, которые легко сломать рефакторингом dependency metadata или weight accounting.
| ID | Invariant | Expected |
|---|---|---|
| S01 | hard reject before score | localScore=null; not rankEligible |
| S02 | N/A excluded | same score as scene without metric |
| S03 | group cap | metric count cannot inflate family influence |
| S04 | missing expected optional group | coverage/confidence ↓; score not injected with zero |
| S05 | missing required + require_all | insufficient_evidence; not rankEligible |
| S06 | score within epsilon | higher confidence may win |
| S07 | feasible exact tie | stable seed/lineage tie-break |
| S08 | both rejected | stable audit ordering; no fake «best design» |
| S09 | same evidence vote + penalty | one representation unless explicit independence |
| S10 | target profile absent | diagnostic-only; no aesthetic localScore |
Correction requests и immutability
Suite проверяет не только результат исправления, но и правильный маршрут изменения.
Scorer mutates path
2.20 не двигает points/vertices и не меняет stroke/color.
Owner request
Диагностика возвращает targetOwner, reason и allowed range.
New branch
2.19 применяет owner correction как новый child candidate с lineage.
Mask / crop / seam regression 2.22
2.21 теперь включает финальный 2.22 в consolidated suite. Проверяется topology и contract boundary, а не конкретный rendering.
| ID | Invariant | Expected |
|---|---|---|
| M01 | open split | two regions + area conservation |
| M02 | reversal | unordered regions same; anchor selection same |
| M03 | crop side | missing keepSide unresolved; never auto-pick by area |
| M04 | ownership | no localBoundaryScore / localScore emitted |
| M05 | 2.14 handoff | PartitionDescriptor / BoundaryRelation canonical |
| M06 | host contract | production HostBoundaryAdapter; RectHostRegion fixture-only |
Context adapter regression 2.23
Context integration проверяется как adapter: глобальные facts приходят извне, а local facts сохраняются неизменными.
| ID | Invariant | Expected |
|---|---|---|
| C01 | no context | effectiveTarget == baseTarget |
| C02 | immutable patch | base target / MetricResult unchanged |
| C03 | conflict | context_conflict; no silent last-write-wins |
| C04 | external selection | local rank/score preserved separately |
| C05 | foreign mutation | owner-routed correction request only |
| C06 | handoff schema | LineStructureCandidate + MaskCropSeamResult canonical types |
Визуальная лаборатория инвариантов
SVG здесь не иллюстрация ради декора. Каждая полоса показывает пару inputs, которые должны дать определённое математическое или архитектурное отношение.
Live reference suite
Inline runner проверяет test-oracle и основные инварианты автономно. В production те же vectors должны запускаться через adapters реальных owner-модулей; этот JS не заменяет production implementations.
Regression protocol
Красный тест — повод понять изменение контракта, а не автоматически ослабить expected.
После изменения любого 2.xx
1. run owner unit tests 2. run 2.21 full suite 3. compare structural signatures 4. compare hard-reject set 5. compare owner/status changes 6. compare metric deltas 7. inspect rank inversions 8. inspect suppression/penalty log 9. inspect lineage determinism 10. accept migration only with reason
Нельзя
rewrite expected because test is red convert N/A to 0 for convenience widen EPS to hide instability duplicate owner formula in test suite assert universal beauty preference allow foreign mutation drop seed/lineage from baseline silently change owner key let global metrics leak into local suite
Baseline manifest
DATA FOR LAYOUT ENGINE
Machine-readable описание test suite. Оно должно позволять подключить реальные module adapters и прогнать тот же набор vectors без чтения всей статьи.
{
"module": "Compo 2.21",
"name": "line_system_reference_regression_suite",
"version": "1.1-consolidated",
"scope": "REFERENCE_AND_REGRESSION_TESTS_FOR_COMPO_2_00_TO_2_23",
"consumes": [
"Compo2.00-Compo2.18 public owner contracts",
"Compo2.19.LineStructureCandidate[]",
"Compo2.19.CandidateLineage[]",
"Compo2.19.GenerationTrace",
"Compo2.20.LineSystemScoreResult",
"Compo2.22.MaskCropSeamResult",
"Compo2.23.LineContextProjection",
"MetricResult[]",
"PenaltyResult[]",
"semanticTarget",
"StylePreset",
"seed",
"ownerVersionMap"
],
"provides": [
"LineRegressionCase[]",
"LineRegressionFixture[]",
"LineRegressionSuite",
"RegressionBaselineManifest",
"SuiteRunReport",
"FailureRecord[]",
"CompatibilityMatrix"
],
"owns_metrics": [],
"descriptors_only": [
"suite pass/fail report",
"fixture hashes",
"baseline signatures",
"failure diagnostics",
"coverage matrix"
],
"hard_constraints": [
"test suite must not become owner of line metrics",
"N/A must never be coerced to zero",
"accepted candidate hardViolation count must be zero",
"global metrics must not affect local line regression results",
"foreign mutation is forbidden",
"same input+seed+versions must reproduce structural output",
"owner key changes require documented migration"
],
"soft_constraints": [
"heuristic metrics use range or relational assertions",
"score drift requires explicit tolerance provenance",
"pixel-perfect generated layout assertions are forbidden"
],
"preferences": [
"derived math uses tight deterministic epsilon",
"composite-first scoring assertions",
"structural signatures over screenshot equality",
"documented migration over silent expected update"
],
"overrides_allowed_by": [
"documented owner corrective pass",
"versioned architecture migration",
"explicit new StylePreset baseline"
],
"initial_heuristics": {
"normalizedGeometryEpsilon": {"value": 0.000001, "provenance": "INITIAL_HEURISTIC"},
"ownerMetricDrift": {"value": 0.02, "provenance": "INITIAL_HEURISTIC"},
"lineSystemScoreDrift": {"value": 0.03, "provenance": "INITIAL_HEURISTIC"}
},
"test_case_schema": {
"id": "string",
"owner": "Compo x.yy | CROSS_MODULE",
"sourceClass": "DERIVED|CONSTRAINT|HEURISTIC_RANGE|RELATIONAL|DETERMINISM|REGRESSION",
"tags": "string[]",
"input": "object",
"expected": "object",
"tolerance": "number|object|null",
"seed": "integer|string|null",
"assertions": "array",
"baselineVersion": "string|null"
},
"suite_policy": {
"runOnEveryLineModuleChange": true,
"covers2_22": true,
"covers2_23": true,
"runBeforeClosingTopic2": true,
"updateExpectedRequiresReason": true,
"pixelPerfectLayoutAssertion": false,
"productionAdaptersRequiredForRelease": true,
"inlineRunnerIsReferenceOracleOnly": true
},
"coverage_required": [
"happy_path",
"N_A",
"scale_invariance",
"sampling_or_segmentation_invariance",
"direction_reversal",
"angle_or_phase_wrap",
"missing_data",
"fallback_confidence",
"ownership",
"no_global_leakage",
"no_double_counting",
"degeneracy",
"deterministic_seed",
"bounded_generation",
"lineage_immutability",
"deterministic_ranking",
"mask_topology",
"crop_explicit_selection",
"host_adapter_contract",
"context_projection_immutability",
"context_conflict_defer",
"external_selection_preserves_local_facts"
],
"live_reference_tests": [
"G01","G02","G03","G04","G05","G06","G07","G08",
"A01","A02","A03","A04","A05","A06","A07","A08","A09","A10",
"O01","O02","O03","O04","O05","O06","O07","O08","O09","O10","O11","O12","O13","O14","O15",
"GEN01","GEN02","GEN03","GEN04","GEN05","GEN06",
"S01","S02","S03","S04","S05","S06","S07","S08","S09","S10",
"R01","R02",
"M01","M02","M03","M04","M05","M06",
"C01","C02","C03","C04","C05","C06"
],
"deferred_inputs": [
"global.balance",
"global.hierarchy",
"global.negativeSpace",
"global.eyeFlow",
"typography.*",
"imageCrop.*",
"posterGlobalScore"
],
"recompute_when": [
"any Compo2.00-Compo2.23 public contract changes",
"owner version changes",
"2.19 generator seed/lineage policy changes",
"2.20 scoring/suppression/ranking policy changes",
"2.22 mask/crop/seam contract changes",
"2.23 context projection/selection contract changes",
"baseline migration is accepted"
],
"finality": "LINE_SUBSYSTEM_REFERENCE_TEST_SUITE_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",
"SAME_INPUT_SEED_VERSIONS_SAME_STRUCTURE"
]
}