moderator-toolbox-nxg-for-reddit / util/wiki/schemas/proposals/schema

util/wiki/schemas/proposals/schema

Functions

canTransition()

canTransition(from, to): boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:475

Returns whether a proposal may move from from to to. Enforced by the mutation layer so a stale write against an already-resolved proposal fails with a typed result instead of silently overwriting a verdict.

Allowed:

  • pending -> any other status.

  • needs_attention -> accepted (retry succeeded), rejected, or obsolete.

  • Terminal statuses -> nothing.

Parameters

from

ProposalStatus

Current status.

to

ProposalStatus

Proposed next status.

Returns

boolean


isReplayClaimActive()

isReplayClaimActive(claim, now): boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:459

Returns whether claim is still active (present and not yet expired) at now. An expired claim is treated as absent so a crashed accept can be retried.

Parameters

claim

ReplayClaim | undefined

The claim to test (or undefined when none is held).

now

number

Current epoch seconds.

Returns

boolean


isTerminalStatus()

isTerminalStatus(status): boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:449

Returns whether a status is terminal (immutable). needs_attention is NOT terminal - it can still be retried into accepted, or moved to rejected/ obsolete.

Parameters

status

ProposalStatus

The status to test.

Returns

boolean

Interfaces

FrozenRemovalBan

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:140

Ban to issue during a removal (resolved values); present only when banning.

Properties

days

days: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:144

Duration in days when not permanent.

note

note: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:146

Ban mod note.

permanent

permanent: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:142

Permanent ban (ignores days).


FrozenRemovalFlair

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:121

Flair to apply during a removal (resolved values); present only when flairing.

Properties

cssClass?

optional cssClass?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:123

templateId?

optional templateId?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:124

text?

optional text?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:122


FrozenRemovalIntent

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:164

The fully-rendered, post-templating intent for a removal-reasons proposal - the first vertical spine. Replay reconstructs the full submission params from this and hands them back to submitRemoval. It stores the resolved values (a reason can be edited/deleted between propose and accept, so ids are NOT re-resolved), but is hand-curated to only what replay needs: empty/default fields are omitted, optional steps (flair/usernote/ban/log) are nested and present only when used, subreddit usernote colors are re-fetched at replay time, and the item’s own metadata (author, permalink, kind, …) is NOT stored - it is re-fetched from the proposal’s itemId at replay (the item still exists until accepted), with the note attributed to the proposal’s proposedBy. The mapping to/from SubmitRemovalParams lives in the removalreasons proposalAdapter (freezeRemovalParams/thawRemovalIntent); thaw’s return type guards that every param the pipeline reads is reconstructed.

Properties

actionLockComment?

optional actionLockComment?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:196

Lock the removal reply comment; omitted when false.

actionLockThread?

optional actionLockThread?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:194

Lock the removed thread; omitted when false.

ban?

optional ban?: FrozenRemovalBan

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:202

Ban to issue; omitted when not banning.

baseLogTitle?

optional baseLogTitle?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:180

Log post title (before {reason} substitution); only when a log sub is set.

flair?

optional flair?: FrozenRemovalFlair

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:184

Flair to apply; omitted when no flair.

logReasonText?

optional logReasonText?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:182

Public log reason substituted into {reason}; only when used.

logSub?

optional logSub?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:178

Log subreddit to cross-post the removal to; only when removal logging is on.

reasonAsSub?

optional reasonAsSub?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:188

Send PM delivery via modmail as the subreddit; omitted when false.

reasonAutoArchive?

optional reasonAutoArchive?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:190

Auto-archive the removal modmail conversation; omitted when false.

reasonCommentAsSubreddit?

optional reasonCommentAsSubreddit?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:192

Post the removal reply as the subreddit (vs the mod); omitted when false.

reasonSticky?

optional reasonSticky?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:186

Sticky the removal reply comment; omitted when false.

reasonText

reasonText: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:166

Final composed reason text, with header/footer and tokens already applied.

reasonTitle?

optional reasonTitle?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:172

Display title(s) of the selected removal reason template(s), joined with “, “ - captured for review so a reviewer can see which reason was chosen at a glance, not just its rendered body. Omitted when no selected reason has a title.

reasonType

reasonType: FrozenReasonType

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:174

Delivery mode for the removal message.

selection?

optional selection?: FrozenRemovalSelection

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:209

The trainee’s structured reason selection, for re-seeding the removal overlay on Edit & Accept. Additive metadata only - replay and display use reasonText. Omitted for captures that predate this field (Edit & Accept falls back to plain Accept when absent).

spam?

optional spam?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:198

Remove as spam (trains the spam filter) rather than a plain removal; omitted when false.

subject

subject: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:176

PM/modmail subject line (used for pm/modmail/ban delivery).

usernote?

optional usernote?: FrozenRemovalUsernote

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:200

Usernote to leave; omitted when none.


FrozenRemovalSelection

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:111

The trainee’s structured reason selection, captured in addition to the composed reasonText, purely to re-seed the full removal overlay for “Edit & Accept”. Display, excerpts, and plain-Accept replay all use reasonText and never touch this.

Properties

includeFooter?

optional includeFooter?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:117

Whether the configured footer was included; present only when a footer is configured.

includeHeader?

optional includeHeader?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:115

Whether the configured header was included; present only when a header is configured.

reasons

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:113

Selected reasons in display order.


FrozenRemovalUsernote

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:128

Usernote to leave during a removal (resolved values); present only when leaving one.

Properties

includeMessage?

optional includeMessage?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:136

Store the removal modmail conversation link on the note.

text

text: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:130

Note body text.

type?

optional type?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:132

Note type/tag key, if any.


FrozenSelectionReason

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:97

One selected reason captured for re-seeding the removal overlay on Edit & Accept. Carries the persistent reason id (so the overlay can re-check it against current config) and the resolved per-reason body (fill-in tokens substituted, inline edits applied) so the overlay shows exactly what the trainee composed.

Properties

id

id: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:99

Persistent RemovalReason.id of the selected template.

text

text: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:101

Resolved per-reason message body (post fill-in substitution / inline edit).

title?

optional title?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:103

The reason’s display title, for the overlay/preview; omitted when none.


NeedsAttentionDetail

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:318

Diagnostic detail recorded when an accept attempt fails partway through replay.

Properties

attemptedAt

attemptedAt: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:322

Epoch seconds when the attempt happened.

attemptedBy

attemptedBy: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:320

Username of the moderator who attempted the accept.

error

error: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:332

Human-readable error text from the failed step.

failedStep

failedStep: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:324

Which replay step failed (e.g. 'removeThing', 'sendRemovalMessage').

irreversibleSideEffect

irreversibleSideEffect: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:330

Whether an irreversible side effect already landed before the failure (e.g. the item was removed but the message send failed). Tells a reviewer whether a naive retry is safe.


Proposal

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:336

A single captured proposal, keyed by its stable Proposal.id.

Properties

ackedByProposer?

optional ackedByProposer?: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:383

Whether the proposer has acknowledged the outcome. Gates pruning: a resolved proposal is kept until acked or until proposalRetentionDays elapses.

action

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:348

The captured action to replay on accept.

feedback?

optional feedback?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:368

Rejecting reviewer’s explanation (reject only).

id

id: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:342

Stable, collision-free id. Generated (not derived from item+time+mod, which collides on rapid double-clicks). itemId/proposedBy/proposedAt are query fields, not identity.

itemId

itemId: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:344

Fullname of the target (e.g. t3_abc, t1_def).

itemKind

itemKind: ProposalItemKind

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:346

Whether the target is a post or comment.

needsAttention?

optional needsAttention?: NeedsAttentionDetail

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:372

Failure diagnostics (needs_attention only).

note?

optional note?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:356

Optional free-text rationale from the proposer.

obsoleteReason?

optional obsoleteReason?: ObsoleteReason

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:370

Why the proposal auto-resolved (obsolete only).

proposedAt

proposedAt: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:352

Epoch seconds when the proposal was created.

proposedBy

proposedBy: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:350

Username of the moderator who proposed the action.

replayClaim?

optional replayClaim?: ReplayClaim

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:378

The in-flight accept claim, set in the same atomic write that begins a replay and cleared when the proposal resolves or the claim is released. Gates concurrent accepts of the same proposal (see ReplayClaim).

resolvedAt?

optional resolvedAt?: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:366

Epoch seconds when the proposal reached a terminal status.

resolvedBy?

optional resolvedBy?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:364

Username of the resolver, or a system sentinel for obsolete.

source

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:354

Why the proposal exists.

status

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:360

Current lifecycle status.

updatedAt

updatedAt: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:362

Epoch seconds of the last mutation to this proposal (any field).


ProposalsData

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:394

The full shape stored on the proposals wiki page.

Single-page layout, confirmed by the Block 0 concurrency probe: Reddit’s /api/wiki/edit honors previous (stale writes get HTTP 409 EDIT_CONFLICT) and read-after-write lag is small (~190 ms), so optimistic concurrency on one page is safe. The bucketed fallback was not needed. See CONCURRENCY_PROBE.md.

Properties

proposals

proposals: Record<string, Proposal>

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:413

All proposals, keyed by Proposal.id.

seq?

optional seq?: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:411

Monotonically increasing page version, bumped by one on every committed write to this page (see mutateProposals). Distinct from ProposalsData.ver (the schema version, which only changes across builds) and from Reddit’s opaque wiki revision id (which isn’t orderable). Because it lives in the data, it travels with the page from any source - a read, a local commit, or a cross-tab broadcast

  • giving display caches a single lag-proof order so they never roll backward. Optional/absent on legacy pages and ad-hoc literals, where it is treated as 0.

Note: a hand-edit or admin revision-restore on the wiki that lowers seq can leave an open tab showing newer-cached data until it reloads - outside the normal mutation flow (every code-path write bumps from the current value) and self-healing on reload.

ver

ver: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:396

Schema version; used by future migrations.


ProposedBan

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:213

A user ban captured for review (itemId is the target username).

Properties

context?

optional context?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:224

Fullname of the thing that prompted the ban, for ban context (optional).

days

days: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:218

Duration in days when not permanent.

message

message: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:222

Ban message sent to the user.

note

note: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:220

Mod note (private).

permanent

permanent: boolean

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:216

Permanent ban (ignores days).

type

type: "ban"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:214


ProposedMute

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:228

A user mute captured for review (itemId is the target username).

Properties

duration?

optional duration?: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:231

Mute duration in days (Reddit’s mute is fixed-length; stored for fidelity).

note?

optional note?: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:233

Mod note (private).

type

type: "mute"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:229


RemovalTarget

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:72

The subset of a thing’s metadata the removal pipeline actually consumes. Kept here (rather than referencing the heavy RemovalReasonsData) so the frozen intent stays small and self-contained, and so submitRemoval can narrow its data parameter to exactly these fields.

Properties

author

author: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:80

Author username (empty when unavailable).

fullname

fullname: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:74

Reddit fullname (e.g. t3_abc123).

kind

kind: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:76

'submission' or 'comment'.

link

link: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:84

Submission link (same as url for posts; parent post link for comments).

logSub

logSub: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:88

Log subreddit name, if removal logging is configured (empty otherwise).

mod

mod: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:86

Username of the acting moderator.

subreddit

subreddit: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:78

Subreddit the thing is in.

url

url: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:82

Permalink of the thing.


ReplayClaim

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:310

A short-lived claim a reviewer places on a proposal in the same atomic write that begins an accept, immediately before its action is replayed. Two reviewers accepting the same proposal would otherwise both replay the (irreversible) side effect before either marked it accepted; persisting the claim makes the conditional wiki write the compare-and-set that lets only one in. Cleared when the proposal resolves or the claim is released; a claim older than REPLAY_CLAIM_TTL_SECONDS is ignored so a crashed/abandoned accept frees the proposal for retry without manual repair.

Properties

at

at: number

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:314

Epoch seconds when the claim was placed.

by

by: string

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:312

Username of the reviewer who holds the claim.

Type Aliases

FrozenReasonType

FrozenReasonType = "reply" | "pm" | "both" | "none"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:64

Delivery mode for a removal message; mirrors removalreasons’ ReasonType.


ObsoleteReason

ObsoleteReason = "deleted" | "already-actioned"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:54

Why a proposal auto-resolved to obsolete.


ProposalItemKind

ProposalItemKind = "post" | "comment" | "user"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:28

What a proposal targets. post/comment are things (itemId is a fullname); user is a user-level action (itemId is the username).


ProposalSource

ProposalSource = "training" | "second-opinion"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:37

Why a proposal exists.

  • training - captured implicitly because the proposer is on the subreddit’s trainee list.

  • second-opinion - captured because the proposer explicitly requested review, regardless of whether training mode is enabled for the subreddit.


ProposalStatus

ProposalStatus = "pending" | "accepted" | "rejected" | "obsolete" | "needs_attention"

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:51

Lifecycle status of a proposal. See isTerminalStatus and canTransition for the allowed transitions.

  • pending - awaiting review.

  • accepted - a reviewer accepted it AND the real action replay fully succeeded.

  • rejected - a reviewer declined it (optionally with feedback).

  • obsolete - auto-resolved without a verdict because the target went away (author deleted it) or was actioned elsewhere.

  • needs_attention - an accept was attempted but replay failed partway; carries NeedsAttentionDetail so a reviewer can decide whether retry is safe.


ProposedAction

ProposedAction = { type: "approve"; } | { spam: boolean; type: "remove"; } | { intent: FrozenRemovalIntent; type: "removal-reason"; } | { type: "lock"; } | { type: "unlock"; } | { sticky: boolean; type: "distinguish"; } | { nsfw: boolean; type: "marknsfw"; } | { num?: number; state: boolean; type: "sticky"; } | ProposedBan | { type: "unban"; } | ProposedMute | { type: "unmute"; } | { cssClass?: string; templateID?: string; text?: string; type: "userflair"; }

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:242

The captured moderation action a proposal represents. Discriminated by type. Thing-targeted: approve/remove/removal-reason/lock/unlock/distinguish/marknsfw/sticky (itemId is a fullname). User-targeted: ban/unban/mute/unmute/userflair (itemId is the username).

Union Members

Type Literal

{ type: "approve"; }

Approve the target.


Type Literal

{ spam: boolean; type: "remove"; }

Remove the target, optionally as spam.


Type Literal

{ intent: FrozenRemovalIntent; type: "removal-reason"; }

Remove via the full removal-reasons composite, replayed from a frozen intent.


Type Literal

{ type: "lock"; }

Lock the target thing.


Type Literal

{ type: "unlock"; }

Unlock the target thing.


Type Literal

{ sticky: boolean; type: "distinguish"; }

Distinguish the target thing (optionally sticky).


Type Literal

{ nsfw: boolean; type: "marknsfw"; }

Mark the target post NSFW (nsfw: false unmarks it).


Type Literal

{ num?: number; state: boolean; type: "sticky"; }

Sticky the target submission into a slot, or unsticky it (state: false).


ProposedBan

Ban the target user.


Type Literal

{ type: "unban"; }

Unban the target user.


ProposedMute

Mute the target user.


Type Literal

{ type: "unmute"; }

Unmute the target user.


Type Literal

{ cssClass?: string; templateID?: string; text?: string; type: "userflair"; }

Set the target user’s flair (text/cssClass/templateID as captured).


ProposedActionType

ProposedActionType = ProposedAction["type"]

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:299

Discriminant string of a ProposedAction.

Variables

defaultProposalsData

const defaultProposalsData: ProposalsData

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:417

Default empty proposals data used when a subreddit has no existing page.


proposalsMaxSchema

const proposalsMaxSchema: 1 = 1

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:22

The maximum proposals schema version this build can read.


proposalsMinSchema

const proposalsMinSchema: 1 = 1

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:20

The minimum proposals schema version this build can read.


proposalsSchema

const proposalsSchema: 1 = 1

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:18

The current proposals schema version written to new wiki pages.


PROPOSED_ACTION_KINDS

const PROPOSED_ACTION_KINDS: object

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:282

The canonical roster of every proposed-action type, classified by how it replays: 'atomic' is replayed inline by the gateway via a single moderation primitive; 'composite' is a multi-step pipeline replayed through a registered handler.

This is the single source of truth that ties the ProposedAction union to its consumers (codec validation, gateway replay, UI labels). The satisfies Record<ProposedAction['type'], ...> makes adding a variant to the union without listing it here a compile error, and each consumer’s exhaustive switch (guarded by assertNever) then fails to compile until it handles the new case - so a new action can never be silently dropped on read or fail only at accept time.

Type Declaration

approve

approve: "atomic" = 'atomic'

ban

ban: "atomic" = 'atomic'

distinguish

distinguish: "atomic" = 'atomic'

lock

lock: "atomic" = 'atomic'

marknsfw

marknsfw: "atomic" = 'atomic'

mute

mute: "atomic" = 'atomic'

removal-reason

removal-reason: "composite" = 'composite'

remove

remove: "atomic" = 'atomic'

sticky

sticky: "atomic" = 'atomic'

unban

unban: "atomic" = 'atomic'

unlock

unlock: "atomic" = 'atomic'

unmute

unmute: "atomic" = 'atomic'

userflair

userflair: "atomic" = 'atomic'


REPLAY_CLAIM_TTL_SECONDS

const REPLAY_CLAIM_TTL_SECONDS: 300 = 300

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:438

How long a replay claim stays valid before the proposal may be reclaimed (seconds).

A normal accept clears its claim explicitly on every outcome - success transitions to accepted, failure to needs_attention, abandonment via an explicit release - so this window only governs the hard-crash case (the holding tab is killed between claiming and resolving). The risk to weigh: if the claim expired mid-replay, a second accept could reclaim and replay the side effect again - exactly the double-action this guards. So the window is set well above any plausible worst-case replay rather than tuned tight: a removal-reason composite chains remove -> message (PM/modmail) -> optional log post -> optional ban -> optional usernote (its own wiki read+write), each a network round-trip with retries, on a slow connection. Five minutes clears that with wide margin; the only cost of erring long is that a genuinely-crashed accept blocks retry until it lapses, which is strictly safer than ever acting twice.


TERMINAL_STATUSES

const TERMINAL_STATUSES: readonly ProposalStatus[]

Defined in: extension/data/util/wiki/schemas/proposals/schema.ts:441

The statuses from which a proposal can never transition again.