fix: never let a guessed threshold hold an account out of sending
The profile check was allowed to set state 'unestablished', which made isSendable() false, which made canAcceptPost() false — so a flagged account never attempted an invitation and the functional probe could never run. The one signal with real authority was gated behind the one that is guessing, and because each check pushed retryAt forward by another interval, accounts that send perfectly well were held indefinitely. Reddit does not publish the thresholds its chat gate applies, so ours are a guess and are now advisory only. Sending is held solely by a live rejection, tracked as sendingHeld on the account summary and keyed off by the attention feed, the sending state and the Accounts page. Reason strings now say "below the configured floor of N" rather than "needs N", and the Accounts page distinguishes "Sending on hold" from "Below thresholds", stating outright that the latter is our own number and the account is still sending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
89eb4583f8
commit
3a40651930
9 files changed
+164
-71
No files matched your search
@@ -23,15 +23,16 @@ const account = {
|
||||
|
||||
const establishment = {
|
||||
state: 'unestablished' as const,
|
||||
reasons: ['Account is 2 days old (needs 7 days)', 'Total karma is 4 (needs 20)'],
|
||||
sendingHeld: true,
|
||||
reasons: ['Account is 2 days old (below the configured floor of 7 days)'],
|
||||
accountAgeDays: 2,
|
||||
linkKarma: 1,
|
||||
commentKarma: 3,
|
||||
totalKarma: 4,
|
||||
hasVerifiedEmail: false,
|
||||
profileCheckedAt: 1_700_000_000_000,
|
||||
lastProbeAt: null,
|
||||
lastProbeFailure: null,
|
||||
lastProbeAt: 1_700_000_000_000,
|
||||
lastProbeFailure: 'Reddit rejected an invitation from this account: account is too new (rate.invitation_limit_new_account)',
|
||||
retryAt: Date.now() + 4 * 60 * 60_000,
|
||||
};
|
||||
|
||||
@@ -95,16 +96,14 @@ describe('accounts page', () => {
|
||||
expect(screen.getByText('Waiting for a refreshed session')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('explains why an unestablished account is not sending, with the profile evidence', async () => {
|
||||
it('explains a held account with what Reddit actually said, plus the profile evidence', async () => {
|
||||
server.use(http.get('/api/accounts', () => HttpResponse.json([{ ...account, establishment }])));
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByText('u/example-account is not established yet.')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Reddit will not accept chat invitations from a new account/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Not established')).toBeInTheDocument();
|
||||
expect(screen.getByText(
|
||||
'Account is 2 days old (needs 7 days); Total karma is 4 (needs 20)',
|
||||
)).toBeInTheDocument();
|
||||
expect(await screen.findByText('u/example-account cannot start chats yet.')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Reddit rejected an invitation from a new account/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Sending on hold')).toBeInTheDocument();
|
||||
expect(screen.getByText(/rate\.invitation_limit_new_account/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Next re-test in about 4 hours/)).toBeInTheDocument();
|
||||
|
||||
const metrics = screen.getByText('Comment karma').closest('dl')!;
|
||||
@@ -113,12 +112,31 @@ describe('accounts page', () => {
|
||||
expect(within(metrics).getByText('No')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not claim an account is blocked when it is only below our own thresholds', async () => {
|
||||
server.use(http.get('/api/accounts', () => HttpResponse.json([{
|
||||
...account,
|
||||
establishment: { ...establishment, sendingHeld: false, lastProbeFailure: null, retryAt: null },
|
||||
}])));
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByText('Below thresholds')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Still sending normally/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/not limits Reddit\s+publishes/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Account is 2 days old (below the configured floor of 7 days)')).toBeInTheDocument();
|
||||
// Nothing may imply sending has stopped.
|
||||
expect(screen.queryByText(/cannot start chats yet/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Outbound sending is on hold/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Sending on hold')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('marks an account that has cleared the thresholds as established', async () => {
|
||||
server.use(http.get('/api/accounts', () => HttpResponse.json([{
|
||||
...account,
|
||||
establishment: {
|
||||
...establishment,
|
||||
state: 'established',
|
||||
sendingHeld: false,
|
||||
lastProbeFailure: null,
|
||||
reasons: [],
|
||||
accountAgeDays: 400,
|
||||
commentKarma: 900,
|
||||
@@ -130,7 +148,7 @@ describe('accounts page', () => {
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByText('Established')).toBeInTheDocument();
|
||||
expect(screen.queryByText(/not established yet/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/cannot start chats yet/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Outbound sending is on hold/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText('1,200')).toBeInTheDocument();
|
||||
});
|
||||
@@ -140,7 +158,8 @@ describe('accounts page', () => {
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByRole('link', { name: 'u/example-account' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('Not established')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Sending on hold')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Below thresholds')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Established')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Comment karma')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user