Policy. New code must pass an originality test:
no GNC function pair may reach ≥ 0.90 multiset Jaccard
(structural near-duplication) unless the pair is on the allowlist.
Enforced by a fast pytest test, so it runs in the default
pytest -m "not slow" suite.
The what and how of the score live in ast_duplication_detector.md; this doc is the policy and the living allowlist log.
The gate scans the whole active tree (GNC/) and fails on
any un-allowlisted high-J pair — it does not try to
diff “new” functions against main. Reasons: - It matches
the repo’s existing “pin the acceptable state, fail on
regression” lineage — the same shape as the five
validate_*_baseline.py gold-standard validators. The
allowlist is the pinned state; an un-allowlisted high-J pair is
the regression. - There is no changed-files/diff plumbing anywhere in
the repo; mapping a git diff hunk back to a function
qualname is fragile (rename detection, line-range → qualname) and
net-new machinery. - A whole-tree scan also catches the case a
diff-on-new-functions gate misses: editing an old function until
it resembles another. - It is cheap — the detector runs in ~1–2
s on GNC/, so the gate is not marked slow.
ORIGINALITY_FAIL = 0.90 # an un-allowlisted pair at/above this FAILS the suite
ORIGINALITY_WARN = 0.85 # advisory only (printed by check_originality.py); never fails
0.90 is deliberately generous: the live
GNC maximum is 0.827 (after the Jun-22 dedup), so the
fail line sits a clear ~0.07 above every current pair and the gate
starts green. A pair only reaches ≥0.90 by being nearly a
copy-paste-rename — the regime where a human almost always agrees “this
is the same code twice.” Below ~0.85 the detector’s known false-positive
families (boilerplate getters, value/derivative twins) dominate, so
0.90 sits comfortably above the noise.
A future violation means a newly-added or
newly-edited function is a structural near-duplicate (≥0.90) of an
existing one and is not adjudicated. Two ways to clear it: 1.
Dedupe (preferred) — extract the shared structure (a
helper, a base method, a Template-Method hook) so the J drops below the
line. 2. Allowlist-with-justification — if the
duplication is deliberate, add the
frozenset({qualnameA, qualnameB}) pair to
ALLOWLIST in
validation/tests/test_originality.py and
add a dated entry to the log below. The PR that adds an allowlist entry
is the review checkpoint.
frozenset of
order-independent frozenset({qualnameA, qualnameB}) pairs.
Key = Func.qualname (dotted ClassName.method)
— line/rename-stable, so the allowlist survives edits above a
function.validation/tests/test_originality.py;
validation/check_originality.py imports it (single source).
This doc only describes it.RHS34b). The current seeds
are unambiguous because each entry names both endpoints. If a
future collision ever matters, upgrade the key to
f"{path}::{qualname}" — escalation path, not pre-built
(YAGNI).All five seeds are below the 0.90 fail line today; they are seeded so
that (a) check_originality.py shows them as adjudicated
rather than unexplained, and (b) if a deliberate fork legitimately
drifts ≥0.90 later, the gate does not false-alarm.
BaseController.calc_posture ↔︎
BreveController.calc_posture (J=0.827). Deliberate
Template-Method fork: the two controllers share a base
([[breve_core_controller]]) but each keeps its own posture leaf (the
base path zeroes what the EE path computes). Must stay independent.BaseController.reconstruct_generalized_velocity ↔︎
BreveController.reconstruct_generalized_velocity (J=0.820).
Same fork — the velocity reconstruction differs in the EE/arm terms;
kept per-subclass by design.BaseController.RHS34b ↔︎
BreveController.RHS34b (J=0.746). Same fork — the working
RHS adds EE feedforward/coupling only on the Breve path.g_vc_desired ↔︎
g_vc_desired_dot (J=0.792, within
analytic_feedforward.py). A quantity and its analytic
time-derivative — structurally similar by construction, not a
copy-paste.BreveController.arm_gain_scale ↔︎
BreveController.base_gain_scale (J=0.737). Two one-line
gates that both wrap the single conditioning_scale ramp for
the arm vs base block.pytest -m "not slow"
picks up validation/tests/test_originality.py (3 tests: the
gate + two anti-rot guards).python validation/check_originality.py (default roots
GNC; pass dirs to widen). Prints the ranked table tagged
per tier (FAIL / OK:allowlisted /
WARN / .) and a
[verdict] PASS|FAIL, exit 0/1.validation/tests/test_originality.py,
validation/check_originality.py.