Remove browsing selection ceilings

This commit is contained in:
jjbrunton committed 2026-08-11 22:54:48 +01:00
1 parent c028637556
commit bc42c55555
14 files changed
+81 -53

No files matched your search

@@ -261,12 +261,12 @@ export class AccountBrowsingSimulationRepository {
}
function validateSettings(settings: AccountBrowsingSettingsRecord): void {
if (!Number.isInteger(settings.dailyTargetMin) || settings.dailyTargetMin < 1 || settings.dailyTargetMin > 12) {
throw new Error('Browsing minimum daily target must be a whole number between 1 and 12');
if (!Number.isSafeInteger(settings.dailyTargetMin) || settings.dailyTargetMin < 1) {
throw new Error('Browsing minimum daily target must be a positive whole number');
}
if (!Number.isInteger(settings.dailyTargetMax)
|| settings.dailyTargetMax < settings.dailyTargetMin || settings.dailyTargetMax > 12) {
throw new Error('Browsing maximum daily target must be between the minimum and 12');
if (!Number.isSafeInteger(settings.dailyTargetMax)
|| settings.dailyTargetMax < settings.dailyTargetMin) {
throw new Error('Browsing maximum daily target must be a whole number no lower than the minimum');
}
if (!Number.isFinite(settings.postWeight) || settings.postWeight < 0
|| !Number.isFinite(settings.commentWeight) || settings.commentWeight < 0
@@ -279,9 +279,8 @@ function validateSettings(settings: AccountBrowsingSettingsRecord): void {
if (!Number.isInteger(settings.jitterMs) || settings.jitterMs < 0) {
throw new Error('Browsing jitter must be a non-negative whole number');
}
if (!Number.isInteger(settings.selectionsPerSession)
|| settings.selectionsPerSession < 1 || settings.selectionsPerSession > 12) {
throw new Error('Browsing selections per session must be a whole number between 1 and 12');
if (!Number.isSafeInteger(settings.selectionsPerSession) || settings.selectionsPerSession < 1) {
throw new Error('Browsing selections per session must be a positive whole number');
}
const { startHour, endHour } = settings.quietHours;
if (!Number.isInteger(startHour) || startHour < 0 || startHour > 23
+34
View File
@@ -619,6 +619,40 @@ export function runMigrations(db: Database.Database): void {
() => {
addColumnIfNotExists(db, 'account_browsing_settings', 'selections_per_session', 'INTEGER NOT NULL DEFAULT 1 CHECK(selections_per_session BETWEEN 1 AND 12)');
},
() => db.exec(`
CREATE TABLE account_browsing_settings_without_ceilings (
account_id TEXT PRIMARY KEY,
enabled INTEGER NOT NULL DEFAULT 0 CHECK(enabled IN (0, 1)),
daily_target_min INTEGER NOT NULL DEFAULT 3 CHECK(daily_target_min >= 1),
daily_target_max INTEGER NOT NULL DEFAULT 6 CHECK(daily_target_max >= daily_target_min),
post_weight INTEGER NOT NULL DEFAULT 60,
comment_weight INTEGER NOT NULL DEFAULT 40,
quiet_hours_json TEXT NOT NULL DEFAULT '{"enabled":true,"startHour":0,"endHour":7}',
minimum_gap_ms INTEGER NOT NULL DEFAULT 2700000,
jitter_ms INTEGER NOT NULL DEFAULT 900000,
allow_nsfw INTEGER NOT NULL DEFAULT 0 CHECK(allow_nsfw IN (0, 1)),
next_due_at INTEGER,
updated_at INTEGER NOT NULL,
auto_vote_enabled INTEGER NOT NULL DEFAULT 0 CHECK(auto_vote_enabled IN (0, 1)),
selections_per_session INTEGER NOT NULL DEFAULT 1 CHECK(selections_per_session >= 1),
FOREIGN KEY (account_id) REFERENCES reddit_accounts(account_id) ON DELETE CASCADE,
CHECK(post_weight >= 0 AND comment_weight >= 0 AND post_weight + comment_weight > 0)
);
INSERT INTO account_browsing_settings_without_ceilings (
account_id, enabled, daily_target_min, daily_target_max, post_weight,
comment_weight, quiet_hours_json, minimum_gap_ms, jitter_ms, allow_nsfw,
next_due_at, updated_at, auto_vote_enabled, selections_per_session
)
SELECT
account_id, enabled, daily_target_min, daily_target_max, post_weight,
comment_weight, quiet_hours_json, minimum_gap_ms, jitter_ms, allow_nsfw,
next_due_at, updated_at, auto_vote_enabled, selections_per_session
FROM account_browsing_settings;
DROP TABLE account_browsing_settings;
ALTER TABLE account_browsing_settings_without_ceilings RENAME TO account_browsing_settings;
`),
];
let currentVersion = db.pragma('user_version', { simple: true }) as number;
+1 -7
View File
@@ -12,7 +12,6 @@ import type { Logger } from '../utils/logger.js';
const MAX_CONTENT_AGE_MS = 7 * 24 * 60 * 60_000;
const MIN_POST_CONTEXT = 12;
const MIN_COMMENT_CONTEXT = 20;
const MAX_DAILY_SELECTIONS = 12;
export interface BrowsingDecisionSink {
recordSimulation(decision: BrowsingDecisionRecord): Promise<void>;
@@ -95,11 +94,6 @@ export class AccountBrowsingSimulationService {
const today = startOfLocalDay(now);
const target = dailyTarget(this.options.accountId, today, settings);
const selectedToday = this.options.repository.countSelectedSince(this.options.accountId, today);
if (selectedToday >= MAX_DAILY_SELECTIONS) {
const nextDueAt = moveOutsideQuietHours(today + 24 * 60 * 60_000, settings);
this.options.repository.setNextDueAt(this.options.accountId, nextDueAt, now);
return { kind: 'scheduled', nextDueAt };
}
if (!force && selectedToday >= target) {
const nextDueAt = moveOutsideQuietHours(today + 24 * 60 * 60_000, settings);
this.options.repository.setNextDueAt(this.options.accountId, nextDueAt, now);
@@ -134,7 +128,7 @@ export class AccountBrowsingSimulationService {
if (candidates.length === 0 && fallbackCandidates.length === 0) return { kind: 'scheduled', nextDueAt };
const sessionLimit = force
? Math.min(settings.selectionsPerSession, MAX_DAILY_SELECTIONS - selectedToday)
? settings.selectionsPerSession
: Math.min(settings.selectionsPerSession, target - selectedToday);
const selected = pickRandom(candidates, sessionLimit, this.random);
if (selected.length < sessionLimit) {
+3 -3
View File
@@ -20,14 +20,14 @@ export const GrowthQuietHoursSchema = z.strictObject({
export const BrowsingSettingsUpdateRequestSchema = z.strictObject({
enabled: z.boolean().optional(),
dailyTargetMin: z.number().int().min(1).max(12).optional(),
dailyTargetMax: z.number().int().min(1).max(12).optional(),
dailyTargetMin: z.number().int().min(1).optional(),
dailyTargetMax: z.number().int().min(1).optional(),
postWeight: z.number().min(0).max(100).optional(),
commentWeight: z.number().min(0).max(100).optional(),
quietHours: GrowthQuietHoursSchema.optional(),
minimumGapMs: z.number().int().min(45 * 60_000).max(DAY_MS).optional(),
jitterMs: z.number().int().min(0).max(6 * 60 * 60_000).optional(),
selectionsPerSession: z.number().int().min(1).max(12).optional(),
selectionsPerSession: z.number().int().min(1).optional(),
allowNsfw: z.boolean().optional(),
autoVoteEnabled: z.boolean().optional(),
});