fb9b4fa422
The §9.1 Slice-2 AI-suggested tag chips, deferred since the propose modal landed, now wired up. As the propose-RFC draft fills in, the backend asks Claude Haiku for tags drawn ONLY from the corpus's existing tag set (the de-facto taxonomy — v1 tags are free-form chip input, there is no curated list), surfaced as clickable suggestion chips. Nothing auto-applies; the user clicks to add. Backend: - tag_suggest.py: universe gather (distinct corpus tags, most-common first), Haiku prompt + tolerant reply parser (drops invented tags, dedupes, clamps confidence), in-process per-user rate limit. - providers.construct_haiku(): dedicated Haiku provider from the operator key, independent of ENABLED_MODELS — tag suggestion always uses the cheap+fast model. No RFC slug at propose time, so the §6.7 funder path does not apply. - POST /api/rfcs/suggest-tags: contributor-gated, rate-limited. Degrades to an empty list (never an error) when no Anthropic key is bound, the corpus has no tags, or the draft is empty. Frontend: - ProposeModal: debounced suggestion fetch (700ms, stale-response guarded), clickable suggestion chips, and the required inline disclosure that the draft text is sent to Anthropic. - api.suggestTags(): forgiving — any non-OK resolves to []. Tests: 11 new (vertical contributor-gating / filtering / no-key / empty-corpus / 429, plus units for gather, parser tolerance, max, short-circuit, provider-failure). Full suite 351 green. New secret on deploy: ANTHROPIC_API_KEY (see CHANGELOG Upgrade steps). Disclosure copy wants a counsel pass before deploy, per #22 discipline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
125 lines
5.0 KiB
Bash
125 lines
5.0 KiB
Bash
# --- Gitea ---
|
|
# Base URL of the Gitea instance the app speaks to.
|
|
GITEA_URL=http://localhost:3000
|
|
|
|
# The bot service account that performs every Git operation per §1.
|
|
# Provision a real Gitea user, generate a personal access token with
|
|
# repo and admin (or at minimum: repo, write:repository) scopes, and
|
|
# put the token here. The bot is the only Git writer.
|
|
GITEA_BOT_USER=rfc-bot
|
|
GITEA_BOT_TOKEN=
|
|
|
|
# The Gitea org or user that owns the meta repo and every RFC repo
|
|
# the bot will create on graduation.
|
|
GITEA_ORG=wiggleverse
|
|
META_REPO=meta
|
|
|
|
# --- OAuth (Gitea) ---
|
|
# In Gitea: Site Administration → Applications → Add OAuth2 Application.
|
|
# Redirect URI: {APP_URL}/auth/callback
|
|
OAUTH_CLIENT_ID=
|
|
OAUTH_CLIENT_SECRET=
|
|
|
|
# --- App ---
|
|
APP_URL=http://localhost:8000
|
|
SECRET_KEY=change-me-to-a-long-random-string
|
|
DATABASE_PATH=data/rfc-app.db
|
|
|
|
# Per §1: owner zero. The Gitea login that gets the owner role on
|
|
# first sign-in.
|
|
OWNER_GITEA_LOGIN=ben
|
|
|
|
# Webhook signature secret. Gitea sends X-Gitea-Signature as the
|
|
# HMAC-SHA256 of the body using this secret. Per §4.1 the webhook is
|
|
# one of two cache writers; signing keeps spurious writes out.
|
|
GITEA_WEBHOOK_SECRET=change-me-to-a-shared-secret
|
|
|
|
# --- LLM providers (carryover §18) ---
|
|
# Comma-separated list of provider keys to enable. Per the §19.2
|
|
# per-RFC-model topic, this is app-wide until that topic lands.
|
|
ENABLED_MODELS=claude
|
|
# ANTHROPIC_API_KEY also powers the §9.1 propose-RFC tag suggestions
|
|
# (roadmap #27) — that surface always uses Claude Haiku for cost,
|
|
# independent of ENABLED_MODELS. With no key set, tag suggestions are
|
|
# simply unavailable (the modal hides the row); the rest of the app is
|
|
# unaffected.
|
|
ANTHROPIC_API_KEY=
|
|
GOOGLE_API_KEY=
|
|
OPENAI_API_KEY=
|
|
|
|
# --- Tag suggestions (§9.1 / roadmap #27) ---
|
|
# Per-user rate limit on the suggest-tags endpoint (cost backstop; the
|
|
# modal debounces and the endpoint is contributor-gated). Optional —
|
|
# these defaults apply when unset.
|
|
TAG_SUGGEST_RATE_MAX=30
|
|
TAG_SUGGEST_RATE_WINDOW_SECONDS=60
|
|
|
|
# --- Email (§15.4) ---
|
|
# Leave SMTP_HOST unset to use the stdout fallback — the integration
|
|
# tests rely on it, and a dev environment without a real SMTP provider
|
|
# still sees envelope traces in the logs. Set the rest to wire a real
|
|
# provider (Postmark, Mailgun, SES, etc.).
|
|
SMTP_HOST=
|
|
SMTP_PORT=587
|
|
SMTP_USER=
|
|
SMTP_PASSWORD=
|
|
SMTP_STARTTLS=1
|
|
|
|
# Single non-spoofing envelope identity per §15.9 — every notification
|
|
# email goes out from the same address regardless of which user's
|
|
# gesture produced it. Configure both the SPF and DKIM records for
|
|
# this address with the chosen SMTP provider.
|
|
EMAIL_FROM=notifications@wiggleverse.local
|
|
EMAIL_FROM_NAME=Wiggleverse
|
|
|
|
# §15.4 bundle threshold: when a user's quiet-hours release queue is
|
|
# at least this big, the flush goes out as one bundled "Activity while
|
|
# you were away" email instead of individual sends.
|
|
EMAIL_BUNDLE_THRESHOLD=5
|
|
|
|
# Set to 0 to suppress every outbound email (the inbox and SSE still
|
|
# work — only the email channel turns off).
|
|
EMAIL_ENABLED=1
|
|
|
|
# --- Email-bounce webhook (§15.4 + §19.2-settled) ---
|
|
# When set, `/api/webhooks/email-bounce` requires the same value in
|
|
# the `X-Webhook-Secret` header. Pick a long random string and
|
|
# configure the SMTP provider's webhook to inject it. When unset,
|
|
# the webhook stays unauthenticated for dev — the v1 contract.
|
|
WEBHOOK_EMAIL_BOUNCE_SECRET=
|
|
|
|
# --- §12 hygiene cadence (Slice 8) ---
|
|
# How often the hygiene scheduler sweeps for the 30/90-day boundaries.
|
|
# Production default is hourly; tests override to seconds via the same
|
|
# env var.
|
|
HYGIENE_TICK_SECONDS=3600
|
|
|
|
# --- v0.7.0: email + one-time-code sign-in (§6.2) ---
|
|
# How long a one-time code stays valid after issuance. Re-requesting
|
|
# invalidates the prior code immediately regardless of TTL.
|
|
OTC_TTL_MINUTES=10
|
|
|
|
# Per-email cooldown between successive /auth/otc/request calls. The
|
|
# endpoint returns HTTP 429 when the cooldown blocks a request (the
|
|
# loud-failure shape so the abuse path is visible). Set to 0 to
|
|
# disable the cooldown — useful for tests but never in production.
|
|
OTC_REQUEST_COOLDOWN_SECONDS=60
|
|
|
|
# --- v0.12.0: CloudFlare Turnstile gate on OTC dispatch (§6.2, item #10) ---
|
|
# Provision a Turnstile site at dash.cloudflare.com → Turnstile → Add
|
|
# site. The site key (public) goes in `frontend/.env` as
|
|
# VITE_TURNSTILE_SITE_KEY. The secret key (private) goes here and is
|
|
# what the backend POSTs to /siteverify alongside the user's response
|
|
# token. Leave both unset for dev/test paths; the gate stays open when
|
|
# the secret is absent AND TURNSTILE_REQUIRED=false (the default).
|
|
CLOUDFLARE_TURNSTILE_SECRET=
|
|
|
|
# When `true`, /auth/otc/request fails closed (HTTP 500 "auth
|
|
# misconfigured") if CLOUDFLARE_TURNSTILE_SECRET is unset. When `false`
|
|
# (the default), a missing secret skips verification — useful in dev
|
|
# and during the pre-rollout window when the operator hasn't wired
|
|
# the secret yet. Flip to `true` once the secret is wired so a future
|
|
# config drift surfaces as a loud 500 rather than a silent abuse-
|
|
# defense disablement.
|
|
TURNSTILE_REQUIRED=false
|