Version: 1.0
Last Updated: February 2026
Author: Technical Architecture Review
- Project Overview
- Architecture
- Storage Design
- Features
- Safety & Policies
- GitHub Integration
- Current Limitations
- Migration Plan
- Future Improvements
Gitcord solves the challenge of automating contributor recognition and task assignment in open-source organizations that use both GitHub and Discord. Organizations need:
- Automated role management based on GitHub contribution activity
- Identity verification between Discord and GitHub accounts without OAuth complexity
- Issue assignment workflows that respect contributor eligibility and mentor oversight
- Transparent audit trails for all automated actions
- GitHub-backed data persistence without external databases (per Bruno's requirement)
Gitcord is built on four foundational principles:
- Runs locally on-demand; no daemon or server required
- All processing happens in a single execution cycle (
run-once) - No external dependencies beyond GitHub and Discord APIs
- Suitable for cron jobs, manual runs, or CI/CD pipelines
- All planned changes are written to JSON and Markdown reports before any mutations
- Reports are generated in
data_dir/reports/audit.jsonandaudit.md - Reviewers can inspect planned role changes, issue assignments, and scoring decisions
- Mutations only occur in
activemode with explicit write permissions
- Identical inputs (same GitHub events, Discord state, config) produce identical plans
- No randomness or time-dependent logic in planning
- Enables reproducible testing and debugging
- Plans are pure functions of input data
- Readers degrade gracefully when API permissions are missing
- Writers check
MutationPolicybefore executing any changes - Failed reads don't crash the system; they produce partial results
- Safe to run with read-only tokens for observation
┌─────────────────────────────────────────────────────────────┐
│ CLI Entry Point │
│ (ghdcbot.cli or Discord Bot) │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Orchestrator │
│ Coordinates: Read → Score → Plan → Report → Apply │
└───────┬───────────────┬───────────────┬──────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Readers │ │ Planners │ │ Writers │
│ │ │ │ │ │
│ GitHubReader │ │ ScoreStrategy│ │ GitHubWriter │
│ DiscordReader│ │ RolePlanner │ │ DiscordWriter│
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────────────┴─────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Storage Layer │
│ SQLite (Primary) + GitHub Snapshots │
└─────────────────────────────────────────────────────────────┘
- Entry point for
run-once,bot,link,verify-link,identity,export-audit - Parses command-line arguments and YAML config
- Builds adapters via plugin registry
- Constructs
Orchestratorand executes commands
- Long-running Discord bot with slash commands
- Handles identity linking (
/link,/verify-link,/verify,/unlink) - Provides contribution metrics (
/summary,/status) - Issue management (
/request-issue,/assign-issue,/issue-requests) - PR previews (
/pr-infoand passive URL detection) - Mentor-only commands (
/sync,/assign-issue,/issue-requests)
- Core execution engine for
run-oncecycle - Coordinates: ingestion → scoring → planning → reporting → mutation
- Manages notification sending (verified-only)
- Writes GitHub snapshots (additive, non-blocking)
- SQLite database for local state (
state.db) - Tables:
contributions,scores,cursors,identity_links,issue_requests,notifications_sent - Append-only audit log (
audit_events.jsonl) - Schema migrations via additive
ALTER TABLE(backward compatible)
- Reads: contributions, issues, PRs via REST API
- Writes: issue assignments, review requests, file commits (snapshots)
- Handles pagination, rate limiting, error recovery
- Filters repos based on config (
repos.mode,repos.names)
- Reads: guild members, roles via REST API
- Writes: role additions/removals, DMs, channel messages
- Handles permission degradation gracefully
- Writes periodic snapshots to GitHub repo as JSON files
- Additive-only: timestamped directories, never overwrites
- Non-blocking: failures don't stop
run-once - Schema versioned (
SCHEMA_VERSION = "1.0.0")
1. Load Config
└─> Parse YAML, load env vars, validate
2. Initialize Storage
└─> Create/upgrade SQLite schema
└─> Load identity mappings (verified from storage, fallback to config)
3. Ingest GitHub Events
└─> Get cursor (last seen timestamp) or use period_start
└─> List repos (filtered by config)
└─> For each repo: fetch issues, PRs, events since cursor
└─> Store events in SQLite `contributions` table
└─> Update cursor to max(created_at)
4. Compute Scores
└─> Load contributions from period_start to period_end
└─> Apply WeightedScoreStrategy (configurable weights)
└─> Support difficulty-based scoring (PR labels)
└─> Upsert scores to SQLite `scores` table
5. Plan Changes
└─> Load Discord member roles
└─> Plan Discord role changes (score-based + merge-based)
└─> Plan GitHub issue assignments (role-based round-robin)
└─> Plan review requests (role-based round-robin)
6. Send Notifications (if enabled)
└─> For each new contribution event:
└─> Check if user is verified (Discord ↔ GitHub)
└─> Check event type matches config
└─> Deduplicate (check `notifications_sent` table)
└─> Send DM or channel message
└─> Record in `notifications_sent`
7. Generate Reports (dry-run/observer modes)
└─> Write `audit.json` (machine-readable)
└─> Write `audit.md` (human-readable)
└─> Write `activity.md` (event feed per repo)
8. Apply Mutations (active mode only)
└─> Apply Discord role plans (add/remove)
└─> Apply GitHub assignment plans (assign issues, request reviews)
└─> All gated by MutationPolicy
9. Write Snapshots (additive, non-blocking)
└─> Collect: identities, scores, contributors, roles, issue_requests, notifications
└─> Write to GitHub repo: `snapshots/YYYY-MM-DDTHH-MM-SS-runid/*.json`
└─> Never blocks run-once completion
Discord bot commands interact with the system as follows:
Identity Linking Flow:
User: /link github_username
└─> IdentityLinkService.create_claim()
└─> Generate verification code
└─> Store in SQLite `identity_links` (verified=0)
└─> Return code to user
User: Adds code to GitHub bio/gist
User: /verify-link github_username
└─> IdentityLinkService.verify_claim()
└─> Fetch GitHub bio/gist via GitHubIdentityReader
└─> Check for code match
└─> Update `identity_links` (verified=1, verified_at=now)
└─> Audit event: identity_verified
Issue Request Flow:
Contributor: /request-issue <issue_url>
└─> Parse issue URL
└─> Verify user is linked (Discord → GitHub)
└─> Store in SQLite `issue_requests` (status='pending')
└─> Audit event: issue_request_created
Mentor: /issue-requests
└─> List pending requests from SQLite
└─> Group by repo
└─> Show embed with eligibility info
Mentor: Clicks "Approve & Assign"
└─> Fetch issue context (GitHub API)
└─> Assign issue via GitHubWriter.assign_issue()
└─> Update `issue_requests` (status='approved')
└─> Send DM to contributor
└─> Audit event: issue_request_approved
Sync Command:
Mentor: /sync
└─> Build Orchestrator (same as run-once)
└─> Execute orchestrator.run_once()
└─> Ingests events, sends notifications, updates roles
└─> Returns success message
Stores raw GitHub contribution events.
CREATE TABLE contributions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
github_user TEXT NOT NULL,
event_type TEXT NOT NULL, -- 'issue_opened', 'pr_opened', 'pr_merged', 'pr_reviewed', 'comment'
repo TEXT NOT NULL,
created_at TEXT NOT NULL, -- ISO-8601 UTC
payload_json TEXT NOT NULL -- JSON blob with event-specific data
);Purpose: Historical record of all GitHub activity. Used for scoring, reports, and snapshots.
Indexes: None (queries filter by created_at range).
Stores computed contribution scores per user per period.
CREATE TABLE scores (
github_user TEXT NOT NULL,
period_start TEXT NOT NULL, -- ISO-8601 UTC
period_end TEXT NOT NULL, -- ISO-8601 UTC
points INTEGER NOT NULL,
updated_at TEXT NOT NULL, -- ISO-8601 UTC
PRIMARY KEY (github_user, period_start, period_end)
);Purpose: Cached scores for role planning. Period boundaries match config scoring.period_days.
Tracks last-seen timestamp per data source.
CREATE TABLE cursors (
source TEXT PRIMARY KEY, -- e.g., 'github'
cursor TEXT NOT NULL -- ISO-8601 UTC timestamp
);Purpose: Incremental ingestion. Prevents re-processing old events.
Stores Discord ↔ GitHub identity mappings.
CREATE TABLE identity_links (
discord_user_id TEXT NOT NULL,
github_user TEXT NOT NULL,
verified INTEGER NOT NULL DEFAULT 0, -- 0=pending, 1=verified
verification_code TEXT,
expires_at TEXT, -- ISO-8601 UTC (for pending claims)
created_at TEXT NOT NULL, -- ISO-8601 UTC
verified_at TEXT, -- ISO-8601 UTC (when verified)
unlinked_at TEXT, -- ISO-8601 UTC (for unlink history)
PRIMARY KEY (discord_user_id, github_user)
);Purpose: Identity verification system. One verified mapping per Discord user. Supports stale refresh.
Indexes:
idx_identity_links_github_user(for reverse lookup)idx_identity_links_verified(for filtering verified)
Stores contributor issue assignment requests.
CREATE TABLE issue_requests (
request_id TEXT PRIMARY KEY, -- UUID
discord_user_id TEXT NOT NULL,
github_user TEXT NOT NULL,
owner TEXT NOT NULL, -- GitHub org
repo TEXT NOT NULL,
issue_number INTEGER NOT NULL,
issue_url TEXT NOT NULL,
created_at TEXT NOT NULL, -- ISO-8601 UTC
status TEXT NOT NULL DEFAULT 'pending' -- 'pending', 'approved', 'rejected', 'cancelled'
);Purpose: Issue request workflow. Mentors review and approve/reject.
Indexes:
idx_issue_requests_status(for filtering pending)idx_issue_requests_created(for sorting)
Deduplication table for notifications.
CREATE TABLE notifications_sent (
dedupe_key TEXT PRIMARY KEY, -- Composite: event_type + github_user + repo + target
event_type TEXT NOT NULL,
github_user TEXT NOT NULL,
discord_user_id TEXT NOT NULL,
repo TEXT NOT NULL,
target TEXT NOT NULL, -- Issue/PR number or identifier
channel_id TEXT, -- NULL = DM, else channel ID
sent_at TEXT NOT NULL -- ISO-8601 UTC
);Purpose: Prevents duplicate notifications for same event.
Indexes:
idx_notifications_sent_github_useridx_notifications_sent_discord_user
Snapshots are written to a GitHub repository (configured via snapshots.repo_path) as JSON files in timestamped directories:
snapshots/
└─ 2026-02-16T23-20-25-abc12345/
├─ meta.json # Schema version, timestamps, run_id
├─ identities.json # Verified Discord ↔ GitHub mappings
├─ scores.json # Current scores per user
├─ contributors.json # Contribution summaries (counts + scores)
├─ roles.json # Discord member roles
├─ issue_requests.json # Pending issue requests
└─ notifications.json # Recent sent notifications (last 1000)
- After
run-oncecompletes successfully (all processing done) - Only if
snapshots.enabled = truein config - Non-blocking: failures are logged but don't stop
run-once - Each snapshot gets a unique
run_id(UUID) for traceability
All snapshot files follow this schema:
{
"schema_version": "1.0.0",
"generated_at": "2026-02-16T23:20:25.123456+00:00",
"org": "example-org",
"run_id": "abc12345-def6-7890-ghij-klmnopqrstuv",
"period_start": "2026-01-17T23:20:25+00:00",
"period_end": "2026-02-16T23:20:25+00:00",
"data": [ /* array of records */ ]
}Example: identities.json
{
"schema_version": "1.0.0",
"generated_at": "2026-02-16T23:20:25+00:00",
"org": "example-org",
"run_id": "abc12345...",
"data": [
{
"discord_user_id": "123456789",
"github_user": "alice"
}
]
}Example: scores.json
{
"schema_version": "1.0.0",
"generated_at": "2026-02-16T23:20:25+00:00",
"org": "example-org",
"run_id": "abc12345...",
"data": [
{
"github_user": "alice",
"period_start": "2026-01-17T23:20:25+00:00",
"period_end": "2026-02-16T23:20:25+00:00",
"points": 42
}
]
}- Never overwrite: Each snapshot is in a unique timestamped directory
- Historical record: All snapshots remain accessible for analysis
- Org Explorer compatibility: External tools can consume snapshots without SQLite
- GitHub as source of truth: Aligns with Bruno's requirement (no Supabase)
Bruno's requirement: "No Supabase, use GitHub for data persistence"
Gitcord's approach:
- ✅ SQLite remains primary source of truth (for now)
- ✅ Snapshots are additive (Phase 1: write-only)
- ✅ Future phases will migrate to GitHub-first (see Migration Plan)
- ✅ No external databases required
- ✅ All data eventually backed to GitHub repo
Purpose: Verify Discord users own their claimed GitHub accounts without OAuth.
Flow:
- User runs
/link github_usernamein Discord - Bot generates 10-character verification code (alphanumeric)
- Code expires in 10 minutes (configurable via
ttl_minutes) - User adds code to GitHub profile bio or a public gist
- User runs
/verify-link github_username - Bot fetches GitHub bio/gist via REST API
- If code found → mark as verified, store
verified_attimestamp - If code not found → return error, user can retry
Security:
- Impersonation protection: one GitHub user can only be verified by one Discord user
- Expired claims are cleaned up automatically
- Stale verification detection (configurable
identity.verified_max_age_days) - Unlink with cooldown (24 hours default)
Storage: identity_links table in SQLite
Commands:
/link- Create claim/verify-link- Verify claim/verify- Check status/status- Show status + roles/unlink- Remove verified link (cooldown applies)
Purpose: Send Discord notifications for GitHub events, but only to verified users.
Event Types:
issue_assigned- User assigned to issuepr_review_requested- User requested as reviewerpr_review_result- PR review approved/changes requested (notifies PR author)pr_merged- User's PR merged
Verification Requirement:
- User must have verified Discord ↔ GitHub link
- Unverified users receive no notifications (anti-spam)
Deduplication:
- Uses
notifications_senttable - Dedupe key:
event_type + github_user + repo + target - Prevents duplicate notifications for same event
Delivery:
- DM (default):
channel_id = null - Channel posting:
channel_idconfigured indiscord.notifications.channel_id
Configuration:
discord:
notifications:
enabled: true
issue_assignment: true
pr_review_requested: true
pr_review_result: true
pr_merged: true
channel_id: null # null = DM, or set channel IDTwo Flows:
- Contributor runs
/request-issue <issue_url> - Bot verifies user is linked (Discord → GitHub)
- Bot stores request in
issue_requests(status='pending') - Mentor runs
/issue-requests - Bot shows list grouped by repo
- Mentor selects repo → sees requests with eligibility info
- Mentor approves/rejects/replaces assignee
- Bot assigns issue on GitHub (if approved)
- Bot sends DM to contributor
- Mentor runs
/assign-issue <issue_url> <discord_user> - Bot resolves Discord user → GitHub username
- Bot shows confirmation embed with issue details
- Mentor confirms → Bot assigns issue on GitHub
- Bot sends DM to assignee (if notifications enabled)
Eligibility Check:
- Role-based: contributor must have role in
assignments.issue_request_eligible_roles - Activity-based: shows merged PR count and last merged time
- Verdict: "eligible", "eligible_low_activity", or "not_eligible"
Two Modes:
- User runs
/pr-info <pr_url> - Bot fetches PR context: title, status, reviews, CI status, last commit
- Bot shows rich embed with all details
- Includes Discord mention if PR author is linked
- Bot monitors configured channels (
discord.pr_preview_channels) - When PR URL detected in message → auto-fetch and post embed
- No command needed, just paste URL
PR Context Includes:
- Repository, PR number, title, state (open/closed/merged)
- Author (with Discord mention if linked)
- Review status (approved/changes_requested/pending)
- CI status (passing/failing/pending)
- Last commit time (relative: "2 hours ago")
- Mentor signal (ready/needs_review/blocked)
Two Rule Types:
- Roles assigned based on contribution score thresholds
- Config:
role_mappings(list ofdiscord_role+min_score) - Example:
Contributorrole at 10 points,Maintainerat 40 points - Scoring period:
scoring.period_days(default: 30 days)
- Roles assigned based on merged PR count
- Config:
merge_role_rules(list ofdiscord_role+min_merged_prs) - Example:
apprenticeat 1 merged PR,testing_roleat 2 merged PRs - Only highest eligible role is assigned (deterministic)
Role Removal:
- Score-based roles removed if score drops below threshold
- Merge-based roles persist (never removed automatically)
- Final desired roles =
max(score_based, merge_based)
Congratulatory Messages:
- When role is added → Bot sends DM congratulating user
- Only in active mode (mutations allowed)
- Fails gracefully if DMs disabled
Purpose: Contributors request issue assignment, mentors review with full context.
Storage: issue_requests table
Statuses:
pending- Awaiting mentor reviewapproved- Mentor approved, issue assignedrejected- Mentor rejectedcancelled- Cancelled (timeout or manual)
Mentor Review UI:
- Shows contributor Discord mention, roles, merged PR count
- Shows issue title, labels, assignees
- Shows eligibility verdict and reason
- Buttons: Approve & Assign, Replace Existing Assignee, Reject, Cancel
Purpose: Append-only log of all system actions.
Storage: data_dir/audit_events.jsonl (JSON Lines format)
Event Types:
identity_claim_createdidentity_verifiedidentity_unlinkedissue_request_createdissue_request_approvedissue_request_rejectedissue_assigned_from_discordsnapshot_writtenreport_generated
Format:
{
"event_type": "identity_verified",
"actor_type": "discord_user",
"actor_id": "123456789",
"timestamp": "2026-02-16T23:20:25+00:00",
"context": {
"github_user": "alice",
"location": "bio"
}
}Export:
- CLI:
ghdcbot --config config.yaml export-audit --format json|csv|md - Filters:
--user,--event-type,--from,--to
Purpose: Write Gitcord state to GitHub repo for external consumption (Org Explorer).
When: After run-once completes successfully
What: 7 JSON files per snapshot:
meta.json- Schema version, timestamps, run_ididentities.json- Verified Discord ↔ GitHub mappingsscores.json- Current scores per usercontributors.json- Contribution summaries (counts + scores)roles.json- Discord member rolesissue_requests.json- Pending issue requestsnotifications.json- Recent notifications (last 1000)
Non-Blocking: Failures are logged but don't stop run-once
Schema Versioning: SCHEMA_VERSION = "1.0.0" (increment on breaking changes)
Purpose: Gate all mutations (Discord roles, GitHub assignments) behind explicit policy.
Structure:
@dataclass(frozen=True)
class MutationPolicy:
mode: RunMode # DRY_RUN, OBSERVER, ACTIVE
github_write_allowed: bool
discord_write_allowed: bool
@property
def allow_github_mutations(self) -> bool:
return mode == ACTIVE and github_write_allowed
@property
def allow_discord_mutations(self) -> bool:
return mode == ACTIVE and discord_write_allowedUsage:
- All writers check
policy.allow_*_mutationsbefore executing - Plans are always generated (for reports)
- Mutations only applied if policy allows
- Reads GitHub and Discord state
- Computes scores and plans
- Generates audit reports
- No mutations (Discord or GitHub)
- Safe for testing and review
- Same as DRY_RUN
- Intended for read-only tokens
- Produces reports without write permissions
- Full execution: reads, plans, and applies mutations
- Requires explicit config:
runtime: mode: "active" github: permissions: write: true discord: permissions: write: true
Plans:
- Dedupe key:
(repo, target_type, target_number, action, assignee) - Prevents duplicate assignments in same run
Notifications:
- Dedupe key:
event_type + github_user + repo + target - Stored in
notifications_senttable - Prevents duplicate notifications for same event
Identity Claims:
- One verified mapping per Discord user
- One verified mapping per GitHub user
- Pending claims expire after 10 minutes
Notifications:
- Only sent to users with verified Discord ↔ GitHub link
- Unverified users receive no notifications (anti-spam)
Issue Requests:
- Contributors must be verified to request assignment
- Mentors can assign to any Discord user (they resolve to GitHub)
Scoring:
- Scores computed for all GitHub users (not just verified)
- But role assignment only applies to verified Discord users
APIs Used:
GET /orgs/{org}/repos- List organization repositoriesGET /repos/{owner}/{repo}/issues- List open issuesGET /repos/{owner}/{repo}/pulls- List open pull requestsGET /repos/{owner}/{repo}/issues/{issue_number}/events- Issue events (assignments, labels)GET /repos/{owner}/{repo}/pulls/{pr_number}/reviews- PR reviewsGET /repos/{owner}/{repo}/commits- Commit history (for PR context)
Event Types Ingested:
issue_opened- New issue createdissue_assigned- Issue assigned to userpr_opened- New PR createdpr_merged- PR mergedpr_reviewed- PR review submitted (approved/changes_requested/comment)comment- Comment on issue/PR
Incremental Ingestion:
- Uses
cursorstable to track last-seen timestamp - Only fetches events since cursor
- Prevents re-processing old events
Repo Filtering:
- Config:
repos.mode(allowordeny) - Config:
repos.names(list of repo names) - Applied before ingestion
API Used:
GET /repos/{owner}/{repo}/contents/{path}- Check if file exists (get SHA)PUT /repos/{owner}/{repo}/contents/{path}- Create/update file
Process:
- Check if file exists (get SHA for update)
- Base64 encode content
- Create commit with message
- Use default branch if not specified
Error Handling:
- Network errors → log warning, return False
- Permission errors → log warning, return False
- Never raises exceptions (non-blocking)
Current Schema Version: 1.0.0
Files:
meta.json- Metadata (schema_version, generated_at, org, run_id, period_start, period_end)identities.json- Array of{discord_user_id, github_user}scores.json- Array of{github_user, period_start, period_end, points}contributors.json- Array of{github_user, period_start, period_end, issues_opened, prs_opened, prs_reviewed, comments, total_score}roles.json- Array of{discord_user_id, roles: [string]}issue_requests.json- Array of{request_id, discord_user_id, github_user, owner, repo, issue_number, issue_url, created_at, status}notifications.json- Array of{dedupe_key, event_type, github_user, discord_user_id, repo, target, channel_id, sent_at}
Schema Evolution:
- Increment
SCHEMA_VERSIONon breaking changes - Consumers should check
schema_versionbefore parsing - Additive changes (new fields) don't require version bump
Raw Event History in Snapshots:
- Snapshots contain aggregated data (scores, summaries)
- Not included: Raw
contributionstable events - Reason: File size concerns (could be large)
- Future: Optional raw event export (see Future Improvements)
SQLite Still Primary Source of Truth:
- Snapshots are additive (write-only)
- SQLite remains authoritative for reads
- Future: Dual-write phase, then gradual SQLite downgrade (see Migration Plan)
No Event Replay:
- Cannot rebuild state from snapshots alone
- Requires SQLite for full history
- Future: Snapshot-based state reconstruction
Limited Snapshot Frequency:
- Snapshots written once per
run-once - No configurable frequency (e.g., hourly, daily)
- Future: Configurable snapshot schedule
Current State:
- Snapshots contain: identities, scores, contributors (aggregated), roles, issue_requests, notifications
- Missing: Raw
contributionsevents (issue_opened, pr_opened, pr_merged, etc.)
Impact:
- Cannot reconstruct full event timeline from snapshots
- Cannot analyze event patterns without SQLite
- Org Explorer cannot show detailed activity feed
Why:
- File size concerns (could be thousands of events per snapshot)
- Schema not yet designed for event export
- Prioritized aggregated data for initial use case
Current State:
- All reads come from SQLite (
state.db) - Snapshots are write-only (additive)
- No read path from GitHub snapshots
Impact:
- System requires SQLite for operation
- Cannot run Gitcord from snapshot-only data
- Migration to GitHub-first requires code changes
Why:
- Incremental migration strategy (see Migration Plan)
- SQLite provides fast local queries
- Snapshots are Phase 1 (additive)
Status: ✅ Implemented
What:
- Snapshots written after each
run-once - Never overwrite previous snapshots
- SQLite remains primary source of truth
- Snapshots are audit output, not input
Goal:
- Establish snapshot schema
- Build Org Explorer compatibility
- Validate snapshot format
Status: 🔄 Planned
What:
- Continue writing to SQLite (backward compatibility)
- Also write to GitHub snapshots (additive)
- Reads still from SQLite
- Snapshots become authoritative for external tools
Goal:
- Validate snapshot reliability
- Ensure no data loss
- Build confidence in GitHub-backed storage
Implementation:
- No code changes needed (already dual-write)
- Focus on validation and monitoring
Status: 🔮 Future
What:
- Option 1: Read from latest snapshot, fallback to SQLite
- Option 2: Reconstruct SQLite from snapshots on startup
- Option 3: Remove SQLite entirely, read from GitHub API
Goal:
- GitHub becomes primary source of truth
- SQLite becomes optional cache
- Eventually: SQLite-free operation
Challenges:
- Performance (GitHub API rate limits)
- Offline operation (requires cache)
- State reconstruction (from snapshots)
Timeline:
- TBD based on Phase 2 validation
Description:
- Add optional raw event export to snapshots
- Include all
contributionsevents in snapshot directory - Format:
events.jsonl(JSON Lines, one event per line)
Benefits:
- Full event timeline in GitHub
- Org Explorer can show detailed activity feed
- Enables event replay and state reconstruction
Challenges:
- File size (could be large)
- Schema design (event format, deduplication)
- Performance (writing large files)
Priority: Medium
Description:
- Ensure snapshot schema matches Org Explorer expectations
- Add metadata fields for Org Explorer consumption
- Document snapshot format for external tools
Benefits:
- Seamless integration with Org Explorer
- Standardized data format
- External tool compatibility
Challenges:
- Schema coordination with Org Explorer
- Versioning strategy
- Backward compatibility
Priority: High (if Org Explorer is target consumer)
Description:
- Finalize snapshot schema (v1.0.0 → v1.0.0 stable)
- Document all fields and types
- Establish versioning policy
Benefits:
- Stable API for consumers
- Clear migration path for schema changes
- Reduced breaking changes
Challenges:
- Balancing flexibility vs. stability
- Handling schema migrations
- Consumer coordination
Priority: Medium
Description:
- Allow snapshots on schedule (hourly, daily) vs. per-run
- Config:
snapshots.frequency: "per-run" | "hourly" | "daily" - Skip snapshots if no changes since last snapshot
Benefits:
- Reduced snapshot volume
- More predictable snapshot timing
- Better for external tool consumption
Challenges:
- Change detection (what counts as "change"?)
- Scheduling (requires daemon or cron)
- Deduplication logic
Priority: Low
Description:
- Reconstruct SQLite state from snapshots
- Command:
ghdcbot --config config.yaml rebuild-from-snapshots - Useful for disaster recovery or migration
Benefits:
- Disaster recovery (if SQLite lost)
- Migration to new instance
- Validation of snapshot completeness
Challenges:
- Handling missing snapshots (gaps)
- Event ordering (if raw events added)
- Performance (processing many snapshots)
Priority: Low
runtime:
mode: "dry-run" | "observer" | "active"
log_level: "INFO"
data_dir: "/path/to/data"
github_adapter: "ghdcbot.adapters.github.rest:GitHubRestAdapter"
discord_adapter: "ghdcbot.adapters.discord.api:DiscordApiAdapter"
storage_adapter: "ghdcbot.adapters.storage.sqlite:SqliteStorage"
github:
org: "example-org"
token: "${GITHUB_TOKEN}" # From env var
api_base: "https://api.github.com"
permissions:
read: true
write: true
discord:
guild_id: "123456789"
token: "${DISCORD_TOKEN}" # From env var
permissions:
read: true
write: true
notifications:
enabled: true
issue_assignment: true
pr_review_requested: true
pr_review_result: true
pr_merged: true
channel_id: null
scoring:
period_days: 30
weights:
issue_opened: 3
pr_opened: 5
pr_reviewed: 2
comment: 1
pr_merged: 10
role_mappings:
- discord_role: "Contributor"
min_score: 10
- discord_role: "Maintainer"
min_score: 40
merge_role_rules:
enabled: true
rules:
- discord_role: "apprentice"
min_merged_prs: 1
snapshots:
enabled: true
repo_path: "org/gitcord-data"
branch: "main"- Adapter: Plugin component (GitHub reader/writer, Discord reader/writer, Storage)
- Claim: Pending identity link (before verification)
- Contribution Event: Raw GitHub activity (issue_opened, pr_opened, etc.)
- Cursor: Timestamp tracking for incremental ingestion
- Deduplication: Preventing duplicate operations (assignments, notifications)
- Identity Mapping: Verified Discord ↔ GitHub link
- Mutation: Write operation (role change, issue assignment)
- Orchestrator: Core execution engine for
run-oncecycle - Plan: Precomputed change (role add/remove, issue assignment)
- Snapshot: GitHub-backed JSON state export
- Verified User: Discord user with verified GitHub link
End of Technical Documentation