ADR 0008: What-if mode bounded input¶
Status: Accepted (2026-04-29)
Context¶
encode_with_custom_train(jd, dial, p, q) lets a user re-encode a dial with arbitrary p / q gear ratio (proxy for missing-gear search; supports the Venus ⅝ exploration etc.). Without bounds, a malicious or curious user could submit p = 9999, q = 9997 and trigger pathological enumeration in pareto_analysis.best_pq_constrained() (used by other bridge methods downstream that share the gear-ratio code path).
Decision¶
encode_with_custom_train validates inputs before calling into the gear-ratio machinery:
if dial not in DIAL_SPECS:
raise ValueError(f"unknown dial {dial!r}")
if not (1 <= p <= 500):
raise ValueError(f"p must be in 1..500, got {p}")
if not (1 <= q <= 500):
raise ValueError(f"q must be in 1..500, got {q}")
if math.gcd(p, q) != 1:
raise ValueError(f"p/q must be reduced (gcd != 1)")
The 500 ceiling is the Greek bronze cuttability ceiling (A-H1 of the H-battery). Beyond it, no historical Greek workshop could have cut the gear; restricting the user to that range matches the device's empirical constraints AND prevents pathological enumeration.
Consequences¶
- Worst-case enumeration is bounded:
O(500²) = 250 000candidate pairs per call. - The bridge layer catches
ValueErrorand returns{"ok": False, "error": ...}. - Future expansion (e.g. ultra-large primes if some hypothetical Greek-engineering paper finds them) requires a PR + ADR update to widen the bounds.
Alternatives considered¶
- Unbounded. Rejected — pathological inputs could starve a Pyodide process for seconds.
- Wider bounds (e.g. 1000). No archaeological evidence for >500-tooth gears; sticking with the documented ceiling.
- Server-side timeout. Doesn't help in Pyodide (single-threaded, no preemption).