Commit graph

14 commits

Author SHA1 Message Date
f2709fc425 fix: match ItemModal stagesForType() to backend valid_stages_for() contract
stagesForType() returned OTHER_STAGES (including 'done') for the unset
type case, but backend stages.py::valid_stages_for(None) only allows
['new'] when type is unset. This let the UI offer 'Done' as a stage
option with no type selected, which the backend rejects with 422.

Also add a watch on type that resets stage to 'new' when the current
stage is no longer valid for the newly selected type, preventing a
stale invalid stage from being silently submitted.
2026-07-13 14:25:28 -07:00
13b41cc438 feat(frontend): add triage list, item modal, and theme 2026-07-13 14:21:45 -07:00
b9c0eda8b5 feat(bot): add subscribe/unsubscribe commands and bot bootstrap 2026-07-13 14:16:28 -07:00
f1bf86a5c9 fix(bot): preserve HTML body content and isolate mailbox poll failures
_extract_body previously discarded real HTML content, replacing it with
a placeholder whenever a multipart email had no text/plain part. Now
falls back to the raw text/html payload before giving up, only using
the placeholder when a message has no text-bearing part at all.

poll_mailboxes also had no error isolation: an unhandled IMAP exception
from any one mailbox would stop the discord.ext.tasks.Loop permanently,
silently killing ingestion for all three mailboxes. Each mailbox's poll
is now wrapped in try/except so one failure doesn't block the others.
2026-07-13 14:12:46 -07:00
f4b57238d2 feat(bot): add email ingestion polling and thread-open triage creation 2026-07-13 14:07:53 -07:00
d86cfd2d3d fix: preserve true message content in raw_content for attachment-only messages
Separate display snippet from raw_content: use actual message.content
for raw_content (which may be empty string) and only use the placeholder
string for the display snippet. This matches the precedent from Task 5's
ping.py where build_ping_embed must never substitute raw_content with a
placeholder, preserving the factual state of whether content was present.
2026-07-13 14:04:48 -07:00
8d8761a79d feat(bot): add manual-share cog forwarding channel messages to #inbox 2026-07-13 14:00:56 -07:00
9971632cd1 fix(bot): fix raw_content round-trip and parsing consistency
- Remove 'or (empty)' fallback in build_ping_embed to preserve empty strings in raw_content
- Fix parse_ping_embed to use direct indexing for all required fields (modality, raw_content, captured_at)
- Remove unused timezone import
- Add test case for empty raw_content round-trip to catch future regressions

Fixes code review findings: empty raw_content was being converted to literal "(empty)" string,
breaking lossless round-trip requirement needed by Task 7's thread-open handler.
2026-07-13 13:56:53 -07:00
af547ab02c feat(bot): add inbox ping embed builder/parser 2026-07-13 13:53:00 -07:00
669bb3456a feat(bot): add modality map, config loader, and backend client
Implements core bot infrastructure:
- modality_map: channel name to modality string mapping (personal_text, voice, fb_messenger, nd_messenger)
- config: Config dataclass with env-driven configuration loading for Discord, backend, and email IMAP credentials
- backend_client: async HTTP client for posting items to backend /items endpoint

All modality_map tests passing (3/3)
2026-07-13 13:48:39 -07:00
a90c3fc172 feat(backend): add FastAPI routes for items
Wraps the Task 2 CRUD layer in a FastAPI app (schemas.py + main.py) with
POST/GET/GET-by-id/PATCH routes for items, matching the exact paths and
status codes the Discord bot's HTTP client will depend on.

Also fixes a latent bug in db.get_engine: sqlite:///:memory: without a
StaticPool gives each new session a fresh, empty database, which broke
as soon as more than one session shared an engine (the API's per-request
session pattern). Tasks 1-2 never hit this because their tests used a
single session per engine.
2026-07-13 13:43:09 -07:00
8f5526c2c0 feat(backend): add idempotent CRUD layer for items 2026-07-13 13:38:36 -07:00
c3e9f2551c feat(backend): add Item model and stage/type validation 2026-07-13 13:33:40 -07:00
337c2a60d1 chore: initialize chorus repo 2026-07-13 11:01:51 -07:00