From c465409aaa5672e3466b6f8744b737fa84993f2b Mon Sep 17 00:00:00 2001 From: Sam Scholten Date: Wed, 7 Oct 2026 15:33:41 +1000 Subject: export: per-feed negative ceiling (--neg-feed-cap-pct) No feed may hold more than 5% (floor 50) of the negative target, counting existing history: uncapped, a few firehoses own the negative class because the read+unstarred pool is sized by feed volume. Enforced by skipping ceiling-full feeds mid seeded permutation walk; shortfall vs the ratio is reported, neg_capped added to the run summary. Also makes bulk mark-read of high-volume feeds safe for training data. --- cmds/negcap_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 cmds/negcap_test.go (limited to 'cmds/negcap_test.go') diff --git a/cmds/negcap_test.go b/cmds/negcap_test.go new file mode 100644 index 0000000..5076163 --- /dev/null +++ b/cmds/negcap_test.go @@ -0,0 +1,25 @@ +// The shared per-feed negative ceiling: uncapped at pct 0, floored so a +// young pool isn't strangled, and clamped to the target itself. +package cmds + +import "testing" + +func TestFeedNegCap(t *testing.T) { + for _, tc := range []struct { + name string + pct, ratio, pos, want int + }{ + {"uncapped at 0", 0, 20, 500, 0}, + {"normal: 5% of 10000-target", 5, 20, 500, 500}, + {"floor wins for young pools", 5, 20, 10, 50}, // target 200, 5% = 10 → floor 50 + {"clamped to the target", 90, 20, 3, 54}, // target 60, 90% = 54 + {"floor can exceed a tiny target", 5, 20, 1, 20}, // target 20 → clamp to 20 + } { + t.Run(tc.name, func(t *testing.T) { + if got := feedNegCap(tc.pct, tc.ratio, tc.pos); got != tc.want { + t.Errorf("feedNegCap(%d, %d, %d) = %d, want %d", + tc.pct, tc.ratio, tc.pos, got, tc.want) + } + }) + } +} -- cgit v1.2.3