Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3e40eb0
doc: plan reviewer assignment acceptance gate (design doc 050)
bryangingechen Jul 9, 2026
88b57ec
feat: add reviewer assignment_acceptance mode (auto/confirm)
bryangingechen Jul 9, 2026
ed1e852
feat: add AssignmentProposal model for the acceptance gate
bryangingechen Jul 9, 2026
8acb4d2
feat: make reviewer assignment builder proposal-aware
bryangingechen Jul 9, 2026
31fdf76
feat: add reviewer assignment propose pipeline + expiry sweep
bryangingechen Jul 9, 2026
f8f63fb
feat: add GitHub-OAuth reviewer console (accept/decline proposals)
bryangingechen Jul 9, 2026
b13e849
docs: surface console deploy settings; enforce .env.example hygiene
bryangingechen Jul 9, 2026
380e66c
feat: add reviewer assignment proposal digest DM (Chunk 5)
bryangingechen Jul 9, 2026
923d544
feat: surface proposed/assigned assignment state (Chunk 7)
bryangingechen Jul 9, 2026
5185a86
docs: converge design doc 050 + AGENTS notes (Chunk 8)
bryangingechen Jul 9, 2026
0d60246
fix: never report console accept as assigned unless it landed
bryangingechen Jul 9, 2026
317bc8f
fix: retire proposal on accept when reviewer has opted out
bryangingechen Jul 9, 2026
a1b26fd
fix: gate reviewer console to already-known users
bryangingechen Jul 9, 2026
8549007
feat: group console proposals by repo; batch queries; local times
bryangingechen Jul 9, 2026
767096b
fix: harden console sign-in against recycled logins and session fixation
bryangingechen Jul 9, 2026
277e8ea
fix: lowercase reviewer login in console decline opt-out
bryangingechen Jul 9, 2026
6ef3fce
fix: enforce apply/propose pipeline mutual exclusion in code
bryangingechen Jul 9, 2026
25d1ae6
fix: block proposals on any PR assignee, matching proposal_validity
bryangingechen Jul 9, 2026
2b47998
refactor: share proposal validity input assembly (sweep + console)
bryangingechen Jul 9, 2026
4af8814
fix: retire opted-out proposals in the shared validity predicate
bryangingechen Jul 9, 2026
160ac91
fix: count pending-proposal load in area stats capacity
bryangingechen Jul 9, 2026
b41c181
fix: clamp only per-reviewer proposal window, not the global setting
bryangingechen Jul 9, 2026
257d993
fix: tie attention-ping suppression to the acceptance's own assignment
bryangingechen Jul 9, 2026
a3d2854
refactor: share one Zulip message chunker across all DM senders
bryangingechen Jul 9, 2026
bb3c492
perf: extract queue-membership facts in SQL, not the full payload
bryangingechen Jul 9, 2026
0190914
refactor: make the console identity resolver resolve-only
bryangingechen Jul 9, 2026
06f5dc5
doc: converge design doc 050 to a final decision record
bryangingechen Jul 10, 2026
4f7839c
feat: add a Zulip `console` command linking to the reviewer console
bryangingechen Jul 11, 2026
22e36a1
refactor: collapse deep-link base settings onto QUEUEBOARD_BASE_URL
bryangingechen Jul 11, 2026
14cb47a
feat: let reviewers self-serve the acceptance-gate mode in prefs
bryangingechen Jul 11, 2026
9c857d0
feat: show reviewer load + assigned PRs in the console; count proposals
bryangingechen Jul 13, 2026
2c0a87e
feat: expand and restyle the reviewer console
bryangingechen Jul 14, 2026
c3bc554
feat: fold proposal DMs into the daily reviewer-attention report
bryangingechen Jul 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 52 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Queueboard Django environment configuration
# Copy to `.env` (or use docker-compose env_file) and adjust values as needed.

# Canonical public base URL of THIS Django site (scheme://host, no trailing path), e.g.
# https://queueboard.example.com. Feature deep-links (reviewer console, Zulip prefs/registration)
# fall back to it, so set this one variable instead of several. NOTE: not the Zulip chat server
# (that is ZULIP_BASE_URL). Leave empty only in local dev.
QUEUEBOARD_BASE_URL=

# Zulip bot (outgoing webhook + API access)
ZULIP_WEBHOOK_TOKEN=
ZULIP_BASE_URL=
Expand All @@ -9,8 +15,6 @@ ZULIP_BOT_API_KEY=
# User credentials for Zulip endpoints that reject bot auth (required for membership checks).
ZULIP_USER_EMAIL=
ZULIP_USER_API_KEY=
# Public base URL used when generating prefs links (e.g. https://queueboard.example.com).
ZULIP_PREFS_URL_BASE=
# Optional dedicated encryption/signing secret for prefs links; falls back to DJANGO_SECRET_KEY when empty.
ZULIP_PREFS_TOKEN_SECRET=
# Salt namespace used for key derivation; changing it invalidates existing links.
Expand All @@ -27,7 +31,6 @@ ZULIP_REGISTRATION_OAUTH_STATE_SALT=zulip_bot.registration.oauth_state
ZULIP_REGISTRATION_OAUTH_STATE_TTL_SECONDS=600

# Zulip assignment command behavior
ZULIP_ASSIGNMENT_SUCCESS_EMOJI=thumbs_up
# Enable live GitHub assign/unassign mutations (true/false, 1/0, yes/no)
ZULIP_ASSIGNMENT_MUTATIONS_ENABLED=

Expand Down Expand Up @@ -99,13 +102,19 @@ GH_TOKEN=
# GITHUB_TOKEN=
# REST API base URL used by OAuth/GitHub App token flows.
GITHUB_API_URL=https://api.github.com
# GitHub OAuth (used by Zulip registration flow)
# GitHub OAuth (used by the Zulip registration flow AND the reviewer console, design doc 050).
# DEPLOY NOTE: both OAuth callbacks are built from QUEUEBOARD_BASE_URL — the registration callback
# is <QUEUEBOARD_BASE_URL>/api/zulip/register/github/callback/ and the console callback is
# <QUEUEBOARD_BASE_URL>/console/oauth/callback/. GitHub requires the redirect_uri to be a
# subdirectory of the OAuth App's registered "Authorization callback URL", so register that callback
# at the site root (https://<host>/) — otherwise sign-in fails with redirect_uri_mismatch.
GITHUB_OAUTH_CLIENT_ID=
GITHUB_OAUTH_CLIENT_SECRET=
GITHUB_OAUTH_REDIRECT_URI=
GITHUB_OAUTH_AUTHORIZE_URL=https://github.com/login/oauth/authorize
GITHUB_OAUTH_TOKEN_URL=https://github.com/login/oauth/access_token
GITHUB_OAUTH_SCOPE=read:user
# Reviewer console: TTL (seconds) for the signed OAuth state round-trip. Default 10 minutes.
CONSOLE_OAUTH_STATE_TTL_SECONDS=600
# Shared secret used to validate GitHub webhook signatures (X-Hub-Signature-256).
GITHUB_WEBHOOK_SECRET=
# JSON object describing GitHub App credentials and operation mapping.
Expand Down Expand Up @@ -269,7 +278,44 @@ ANALYZER_REVIEWER_ASSIGNMENT_APPLY_DEDUPE_DAYS=7
# default bounds GitHub secondary-rate-limit exposure and drains a cutover backlog
# gradually (capped-over proposals are retried next run).
ANALYZER_REVIEWER_ASSIGNMENT_APPLY_MAX_PER_REPO=25
# Reviewer attention daily sweep controls.
# Reviewer assignment acceptance gate (design doc 050) — builder/engine tuning.
# PENDING_LOAD_WEIGHT: weighted load a pending (proposed) proposal adds to the reviewer
# (a proposal occupies a slot, like an AwaitingReview PR); it also excludes the PR from
# re-proposal. EXPIRE_COOLDOWN_DAYS: skip a reviewer for a PR whose proposal they let
# expire within this many days (a soft cooldown, not a permanent opt-out; 0 disables it).
ANALYZER_ASSIGNMENT_PROPOSAL_PENDING_LOAD_WEIGHT=1.0
ANALYZER_ASSIGNMENT_PROPOSAL_EXPIRE_COOLDOWN_DAYS=14
# Acceptance-gate rollout flags (design doc 050), each independently toggleable.
# All default off; enable EITHER this gate OR the legacy
# ANALYZER_REVIEWER_ASSIGNMENT_APPLY_* task, not both (propose supersedes
# apply: it direct-assigns auto-mode reviewers itself and proposes to confirm-mode ones).
# If both are enabled, the apply task skips itself (with an error log) so the gate holds.
# ENABLED: propose task creates proposals / direct-assigns. ASSIGN_ON_ACCEPT_ENABLED:
# console accept performs the GitHub assign. DRY_RUN: compute + record would-do outcomes
# with no side effects. Proposal DMs ride the daily reviewer-attention report, so also
# enable ANALYZER_REVIEWER_ATTENTION_ENABLED + ANALYZER_REVIEWER_ATTENTION_DELIVERY_ENABLED.
ANALYZER_ASSIGNMENT_PROPOSALS_ENABLED=0
ANALYZER_ASSIGNMENT_PROPOSALS_ASSIGN_ON_ACCEPT_ENABLED=0
ANALYZER_ASSIGNMENT_PROPOSALS_DRY_RUN=0
# CONSOLE_UNASSIGN_ENABLED: the console assigned-PR roster becomes a form letting a reviewer remove
# themselves from PRs (self-service only; it never unassigns anyone else). Off by default.
ANALYZER_ASSIGNMENT_PROPOSALS_CONSOLE_UNASSIGN_ENABLED=0
# Acceptance window in days (a proposal expires this long after creation unless accepted);
# the per-reviewer notification_settings override is clamped to >= 7.
ANALYZER_ASSIGNMENT_PROPOSAL_WINDOW_DAYS=7
# On-queue-exit policy for a pending proposal: invalidate (default) | retain.
ANALYZER_ASSIGNMENT_PROPOSAL_ON_QUEUE_EXIT=invalidate
# Propose task schedule (daily; default 00:45 UTC, superseding the legacy apply slot).
# PERIOD_SECONDS<=0 disables scheduling.
ANALYZER_ASSIGNMENT_PROPOSE_PERIOD_SECONDS=86400
# ANALYZER_ASSIGNMENT_PROPOSE_UTC_HOUR=0
# ANALYZER_ASSIGNMENT_PROPOSE_UTC_MINUTE=45
# Expiry/reconcile sweep period (essential maintenance; runs regardless of the master switch).
# Seconds; <=0 disables it.
ANALYZER_ASSIGNMENT_PROPOSAL_EXPIRY_PERIOD_SECONDS=3600
# Reviewer attention daily sweep controls. This DM also carries the acceptance-gate
# "Proposed to you" section (design doc 050): an un-notified pending proposal triggers a
# send even for reviewers with notifications_enabled off (proposal prompts are transactional).
# ENABLED: run the sweep task at all (computes reports/counters).
ANALYZER_REVIEWER_ATTENTION_ENABLED=0
# ENFORCEMENT_ENABLED: execute GitHub auto-unassign for threshold hits.
Expand Down
9 changes: 7 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ Notes
- Copy `.env.example` to `.env` for local Django work; supply database credentials, GitHub tokens, and task runner settings as described in `docs/django_backend_plan.md`.
- Run the stack through `docker compose` against PostgreSQL; we no longer support SQLite fallbacks for quick tests.
- Keep secrets out of version control—store them in the `.env` file or your chosen secret manager.
- **When adding a new Django setting backed by an env var**: always add it in both `qb_site/qb_site/settings/base.py` (as `FOO = os.getenv("FOO", ...)`) *and* `.env.example` (with a comment). Omitting either means the setting silently has no effect in production or is undiscoverable for new deployments.
- **Every configurable setting MUST be wired through `base.py` AND documented in `.env.example` — no exceptions.** This is forgotten often; treat it as part of "done" for any setting change:
1. Define it in `qb_site/qb_site/settings/base.py` as `FOO = os.getenv("FOO", <default>)` (with a short comment).
2. Add `FOO=<default-or-blank>` to `.env.example` with a comment explaining it.
- **Antipattern that silently breaks this:** reading `getattr(settings, "FOO", default)` in code *without* a matching `os.getenv` line in `base.py`. That "phantom" setting can never be configured — it is always the hardcoded default — and is invisible to `.env.example`. If a value is meant to be tunable, wire it through `base.py`; if it is a true constant, make it a module-level constant, not a `getattr(settings, ...)`.
- Consequence of skipping either step: the setting has no effect in production (missing from `base.py`) or is undiscoverable for new deployments (missing from `.env.example`).
- When a setting also requires a live-deployment change (base URL, OAuth callback, secrets), surface it in the relevant runbook under `docs/` too (e.g. `docs/zulip_github_oauth_setup.md`).

## Containers & Volumes
- Code is bind-mounted read-only into containers (`.:/app:ro`) to avoid writes into the repo from inside Docker.
Expand All @@ -63,7 +68,7 @@ Notes
## Keeping AGENTS.md Files Updated
- Every directory with significant logic has its own `AGENTS.md` (mirrored as `CLAUDE.md`).
Current locations: root, `qb_site/`, `qb_site/syncer/`, `qb_site/analyzer/`,
`qb_site/zulip_bot/`, `src/queueboard/`.
`qb_site/zulip_bot/`, `qb_site/console/`, `src/queueboard/`.
- When you add, rename, or remove management commands, Celery tasks, key services, or
directory structure, update the relevant `AGENTS.md` in the same commit/PR.
- When you add a new app or significant sub-directory, create a matching `AGENTS.md`
Expand Down
Loading