# Allowlist for scripts/check_stimulus_sources.py -- a RATCHET.
#
# One private stimulus or measurement per line, outside the sanctioned home
# (the library: wfmgen for signal, ber for metrics), each with a reason.
#
# THIS LIST MAY ONLY SHRINK. Adding an entry means a new private copy of
# something doppler already ships -- and a private copy does not just
# duplicate code, it invents a CONVENTION (a level, a window, a scale),
# which is the part that goes wrong silently.
#
# Format:  <marker>::<path>::<normalised source line> | reason
#
# NOT NEEDED HERE: a function that WRAPS the library. The gate reads
# bodies, not just names, and follows delegation transitively through
# module-level helpers -- so `evm_db()` built on ber_evm_db(), or an
# `rrc_bpsk()` that reaches rrc_taps through two helpers, is recognised as
# the sanctioned home and never reported. Do not add such a function here:
# an allowlist entry for correct code reads as debt and dilutes the ratchet.
#
# ── SAFE: an independent ORACLE ───────────────────────────────────────
# A theory test compares a MEASURED curve against an analytic model. The
# model has to be derived independently or the comparison is circular --
# the same carve-out .phase-conversion-allow makes for oracles. These are
# not copies the rule is about.

pulse::src/doppler/examples/symsync_theory_demo.py::def _rc(t): | SAFE: independent ORACLE, the published twin of test_theory_symsync's reference. Same argument: the page's whole point is measurement against an analytic model derived independently.
pulse::src/doppler/track/tests/test_theory_symsync.py::def _rc(t): | SAFE: independent ORACLE. _reference_scurve() builds the analytic Gardner S-curve that SymbolSync's MEASURED one is correlated against (>0.99). Generating the reference with the library would make the test partly circular -- an oracle that cannot express the pulse cannot check the pulse.

# ── KNOWN VIOLATIONS: recorded as debt, NOT endorsed ──────────────────
# Each is a private copy of something the library already provides. They
# are listed so the gate can stop NEW ones appearing while these are
# outstanding; the ratchet's whole purpose is that removing them is the
# only permitted direction. Do not add to this section.

pulse::src/doppler/track/tests/test_symsync.py::def _rc(t, beta, T): | VIOLATION (review). A raised cosine taking (t, beta, T) -- unlike its theory sibling this is the FUNCTIONAL suite, so it is most likely stimulus rather than an oracle. Confirm which before removing; if it is an oracle, move it to the SAFE section with that reason.

# ── SAFE: not a measurement at all ────────────────────────────────────
# The widened `evm` marker (a `\w*` prefix, added after `self_evm_db()` hid
# a line-for-line twin of ber_evm_db behind one word) reaches names that
# only PASS an EVM around. They compute nothing, so there is nothing for
# the library to own.

evm::src/doppler/examples/mpsk_receiver_performance_demo.py::def panel_evm(ax, trials): | SAFE: a matplotlib panel. It plots t["evm"] values measured elsewhere; it does not compute one.
evm::src/doppler/tests/_validation_common.py::def clamp_evm_db(evm: float) -> float: | SAFE: floors an already-measured EVM at EVM_FLOOR_DB. Its own docstring says the argument arrives "as ber_evm_db reports it" -- it is downstream of the kernel, not a copy of it.

# ── SAFE: the library deliberately refuses this measurement ───────────
# ber_evm_db is SELF-referenced with no truth and no lag, and says so:
# "cannot be fooled by an alignment search". Minimising over alignment is
# exactly what these need, so they are the opposite measurement rather than
# a private copy of this one.

evm::src/doppler/resample/tests/test_RateConverter.py::def _best_evm_db(y: np.ndarray, syms: np.ndarray) -> float: | SAFE: TRUTH-referenced and minimised over strobe alignment. Open loop the cascade's strobe phase is arbitrary, so minimising isolates the matched filter from the timing loop that does not exist yet -- the one thing ber_evm_db is built to refuse.
evm::src/doppler/resample/tests/test_RateConverter.py::def _sweep_evm_db(sps: float, compensate: int) -> float: | SAFE: delegates to _best_evm_db above, which the gate cannot follow because that helper is itself not a sanctioned name. Same reason as its callee.
