# See docs/standards/adr_enforcement/standard.yaml for what `id` and
# `enforced_by` mean.
#
# The rest of this file is the normative encoding of the two tables in
# README.md that make factual claims: the three-layer table and the
# "When each layer is required" matrix. The prose around them stays prose —
# it is how-to, not a claim a machine can check.
#
# Two writers, one set: editing a table without editing this file (or the
# other way round) reddens
# tests/architecture/test_testing_standard_drift.py.
id: testing
enforced_by:
  - tests/architecture/test_mockworld_loop_scenario_ratchet.py
  - tests/architecture/test_testing_standard_drift.py

# The three layers, in the order both README tables read left to right.
# `column` is the matrix header; `label` is the bolded name in the layer
# table; `where` must be how that table's Where cell opens.
layers:
  - id: unit
    label: Unit
    column: Unit
    where: "tests/test_*.py"
  - id: scenario
    label: MockWorld scenario
    column: Scenario
    where: "tests/scenarios/test_*_scenario.py"
  - id: sandbox
    label: Sandbox e2e
    column: Sandbox
    where: "tests/sandbox_scenarios/scenarios/sNN_*.py"

# One entry per row of "When each layer is required". `shape` is the row's
# first cell, verbatim (the `readme_row` convention from
# docs/standards/factory_autonomy/policy.yaml).
#
#   required     -> the cell opens with ✅
#   conditional  -> the cell opens with ⚠️
#   not_required -> the cell opens with ❌
requirements:
  - shape: "New port method (e.g. `update_pr_branch`)"
    unit: required
    scenario: required
    sandbox: required
  - shape: "New loop or runner"
    unit: required
    scenario: required
    sandbox: required
  - shape: "New phase decoration / cross-cutting concern (OTel, telemetry)"
    unit: required
    scenario: required
    sandbox: conditional
  - shape: "Pure refactor with no behavior change"
    unit: required
    scenario: not_required
    sandbox: not_required
  - shape: "Bug fix"
    unit: required
    scenario: required
    sandbox: conditional
  - shape: "New ADR / wiki / config"
    unit: not_required
    scenario: not_required
    sandbox: not_required

# Each normative rule in README.md names the check that catches its violation.
# Properties: tests/architecture/test_standards_rules_are_wired.py

rules:
  - id: every-loop-has-a-scenario
    claim: Every registered loop is driven by at least one MockWorld scenario
    enforced_by: tests/architecture/test_mockworld_loop_scenario_ratchet.py::test_every_loop_is_driven_by_a_mockworld_scenario
  - id: scenario-coverage-only-shrinks
    claim: The count of loops with no scenario is ratcheted shrink-only
    enforced_by: tests/architecture/test_mockworld_loop_scenario_ratchet.py::test_the_ratchet_only_shrinks
  - id: the-needle-rejects-an-undriven-loop
    claim: The coverage needle genuinely rejects a loop nothing drives, so the ratchet is not vacuous
    enforced_by: tests/architecture/test_mockworld_loop_scenario_ratchet.py::test_the_coverage_needle_rejects_a_loop_nothing_drives
  - id: layer-table-matches-the-declaration
    claim: The README layer table lists exactly the declared layers, in the declared order
    enforced_by: tests/architecture/test_testing_standard_drift.py::test_the_table_lists_exactly_the_declared_layers_in_order
  - id: scenarios-do-not-swap-fakes-for-raw-mocks
    claim: A MockWorld scenario asserts GitHub side effects through FakeGitHub state, not raw mocks
    enforced_by: tests/architecture/test_mockworld_scenario_fake_boundaries.py::test_mockworld_scenarios_assert_github_side_effects_through_fake_state

readme_mentions_not_enforcers:
  - path: tests/scenarios/test_caretaker_loops_part2.py
    why: Cited as the canonical EXAMPLE of a Pattern B direct-instantiation scenario, not as an enforcer of this standard.
