Claude/add claude documentation k8vv j#88
Conversation
- gnat/review/: ReviewItem dataclass, ReviewQueueStore (SQLAlchemy), ReviewService (submit/approve/reject/modify/promote/bulk ops/stats) - gnat/serve/routers/review.py: 8 REST endpoints; registered in app.py - gnat/tui/screens/review.py: ReviewScreen (F6) with stats bar, DataTable, detail pane, approve/reject actions - gnat/tui/app.py: 6th tab (Review F6), db_url param propagated - gnat/cli/main.py: gnat review list/approve/reject/stats subcommands - tests/unit/review/: 40 tests covering full service/store/model layer - tests/unit/test_tui.py: F6 binding, 6-tab assertion, ReviewScreen import - CHANGELOG.md: document review queue + STIX object validation additions https://claude.ai/code/session_01BDoue9HxB83ijLzFARAugq
Full ConnectorMixin implementation for Discord REST API v10: - authenticate() sets Bot token auth header - health_check() via /gateway/bot - get_object/list_objects/upsert_object/delete_object for messages, channels, threads, and guild members - STIX translation: messages→note, channels→observed-data, users→identity with x_discord extension on all objects - Domain helpers: post_message, list_messages, start_thread, list_archived_threads, get_user, list_members - Registered as "discord" in CLIENT_REGISTRY - 52 unit tests covering all public methods and STIX translation - config/config.ini.example: [discord] section added https://claude.ai/code/session_01BDoue9HxB83ijLzFARAugq
There was a problem hiding this comment.
Pull request overview
This PR introduces two major feature areas to GNAT: (1) a Discord Bot connector integrated into the connector registry/config, and (2) an AI-extracted intel review queue with persistence, CLI, REST API, and a new TUI “Review” tab (F6).
Changes:
- Add a full Discord REST API v10 connector (
DiscordClient) and register it inCLIENT_REGISTRY, with config example + unit tests. - Add a new
gnat.reviewpackage (models/store/service), plus FastAPI router and CLI subcommands to manage the review workflow and promotion. - Extend the Textual TUI to include a new “Review” tab/screen and update related unit tests and changelog.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 26 comments.
Show a summary per file
| File | Description |
|---|---|
tests/unit/test_tui.py |
Updates TUI tests for the new Review tab/F6 binding and adds a 6-tab assertion. |
tests/unit/review/test_review.py |
Adds unit tests for the new review queue model/store/service. |
tests/unit/review/__init__.py |
Adds package marker for review unit tests. |
tests/unit/connectors/test_connectors.py |
Adds a Discord connector test suite (CRUD, helpers, STIX translation, registry). |
gnat/tui/screens/review.py |
Implements the new Textual “Review” screen (queue browsing + approve/reject UX). |
gnat/tui/app.py |
Adds the Review tab pane and F6 binding to the TUI app. |
gnat/serve/routers/review.py |
Adds a FastAPI router exposing the review queue endpoints. |
gnat/serve/app.py |
Registers the new review router with the FastAPI app. |
gnat/review/store.py |
Adds SQLAlchemy-backed persistence for the review queue. |
gnat/review/service.py |
Adds review workflow business logic (submit/approve/reject/modify/promote/bulk/stats). |
gnat/review/models.py |
Adds ReviewItem and ReviewStatus model definitions + serialization helpers. |
gnat/review/__init__.py |
Exposes the review package public API. |
gnat/connectors/discord/connector.py |
Replaces placeholder with a full Discord REST connector implementation. |
gnat/connectors/discord/__init__.py |
Exposes DiscordClient from the discord connector package. |
gnat/clients/__init__.py |
Registers discord in CLIENT_REGISTRY. |
gnat/cli/main.py |
Adds gnat review CLI subcommands (list/approve/reject/stats) and TUI screen choice. |
config/config.ini.example |
Adds example [discord] configuration section. |
CHANGELOG.md |
Documents the Discord connector and review queue additions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from gnat.tui.screens.review import ReviewScreen | ||
| from gnat.tui.screens.scheduler import SchedulerScreen | ||
|
|
||
| assert all([QueryScreen, LibraryScreen, SchedulerScreen, ReportsScreen, InvestigationsScreen]) |
There was a problem hiding this comment.
ReviewScreen is imported here but not referenced in the assertion, which will trigger Ruff F401 (unused import) for this test function. Either include ReviewScreen in the assert all([...]) list or remove the import.
| assert all([QueryScreen, LibraryScreen, SchedulerScreen, ReportsScreen, InvestigationsScreen]) | |
| assert all( | |
| [ | |
| QueryScreen, | |
| LibraryScreen, | |
| SchedulerScreen, | |
| ReportsScreen, | |
| ReviewScreen, | |
| InvestigationsScreen, | |
| ] | |
| ) |
| from __future__ import annotations | ||
|
|
||
| import pytest | ||
| from datetime import datetime, timezone |
There was a problem hiding this comment.
datetime/timezone are imported but unused in this test module, which will fail Ruff F401. Please remove the unused import(s) or use them in the tests.
| from datetime import datetime, timezone |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json |
There was a problem hiding this comment.
json is imported but never used in this module; Ruff will flag this as F401. Remove the unused import (or use it).
| import json |
| if item.reviewer_notes: | ||
| self.query_one("#detail-notes-input", Input).value = item.reviewer_notes |
There was a problem hiding this comment.
When switching between items, reviewer notes are only set if item.reviewer_notes is truthy; selecting an item with no notes will leave the previous item's notes in the input. Set the notes input value unconditionally (e.g., to item.reviewer_notes or "") to avoid stale UI state.
| if item.reviewer_notes: | |
| self.query_one("#detail-notes-input", Input).value = item.reviewer_notes | |
| self.query_one("#detail-notes-input", Input).value = item.reviewer_notes or "" |
| if thread_id: | ||
| body["thread_id"] = thread_id | ||
| if embeds: | ||
| body["embeds"] = embeds | ||
| return self.post(f"{_API_BASE}/channels/{channel_id}/messages", json_body=body) |
There was a problem hiding this comment.
BaseClient.post() accepts json= (see gnat/clients/base.py), not json_body=. Passing json_body will raise TypeError at runtime. Update these calls to use json=body (and adjust tests accordingly).
| """ | ||
| gnat.review.store | ||
| ================== | ||
| SQLAlchemy-backed persistence for the AI-extracted intel review queue. | ||
|
|
There was a problem hiding this comment.
This new module is missing the project-standard SPDX/copyright header (compare e.g. gnat/serve/routers/reports.py:1-2). Please add the SPDX-License-Identifier and copyright lines at the top for consistency/licensing hygiene.
| ### Added — Discord Bot Connector | ||
|
|
||
| **`gnat/connectors/discord/connector.py`** — full `ConnectorMixin` implementation | ||
| - `DiscordClient(BaseClient, ConnectorMixin)` targeting Discord REST API v10 | ||
| - `authenticate()`: sets `Authorization: Bot <token>` + `Content-Type: application/json`; normalises bare token (adds `Bot ` prefix) |
There was a problem hiding this comment.
The PR title suggests Claude documentation work, but this changelog entry documents substantial new functionality (Discord connector + review queue). Consider updating the PR title/description to accurately reflect the scope to avoid confusion during release/review.
|
|
||
| import json | ||
| import logging | ||
| from datetime import datetime, timezone |
There was a problem hiding this comment.
datetime/timezone are imported but unused in this module, which will fail Ruff F401. Please remove the unused import(s).
| from datetime import datetime, timezone |
| if stix_type in ("note", "indicator"): | ||
| parts = object_id.split(":", 1) | ||
| if len(parts) != 2: | ||
| raise GNATClientError( | ||
| f"object_id must be '<channel_id>:<message_id>' for stix_type '{stix_type}'" | ||
| ) | ||
| channel_id, message_id = parts | ||
| msg = self.get_message(channel_id, message_id) | ||
| return self.to_stix(msg) |
There was a problem hiding this comment.
get_object("indicator", ...) currently returns self.to_stix(msg), which produces a STIX note (not an indicator). This breaks the expectation that get_object returns an object of the requested STIX type, and makes the indicator branch indistinguishable from note. Consider either (a) returning an actual STIX Indicator here (if IOC extraction is available), or (b) removing indicator support and requiring callers to fetch note then extract IOCs separately.
| """ | ||
| gnat.review | ||
| ============ | ||
| AI-extracted intel review and promotion queue. | ||
|
|
There was a problem hiding this comment.
This new module is missing the project-standard SPDX/copyright header (compare e.g. gnat/tui/screens/query.py:1-2). Please add the SPDX-License-Identifier and copyright lines at the top for consistency/licensing hygiene.
No description provided.