Add MappingService (work_record_uuid ↔ external id), project/map CLI with outward push-status, optional TaskSpec work_record fields, boundary-sync policy, and packaging/capability framing cleanup. Mark WP-0005 finished.
15 KiB
Issue Core — External Tracker Connector
A backend-agnostic connector to third-party issue trackers (Gitea/Forgejo, GitHub, …), not the fleet's internal work-origin or coordination substrate.
Purpose
issue-core is a standardized abstraction layer for talking to external issue-tracking backends (Gitea, GitHub, GitLab, local SQLite cache). Instead of each agent or automation implementing platform-specific APIs, they use one consistent CLI and Python interface — and, going forward, a durable work-record UUID ↔ external issue id mapping when a tracker projection is switched on.
Fleet coordination vs external trackers
Internal fleet work is not originated here. Work records (workplans, tasks, intake, decisions, …) live as repo files (ADR-001) and are indexed by state-hub under the work-record types canon — see the-custodian/canon/standards/work-record-types_v0.1.md and the-custodian/research/WorkOrchestrationArchitectureDraft.md §4.2.
issue-core's role (founder-reviewed, 2026-07-20):
- Connector to external issue-tracking systems when collaboration requires them
- Mapping between internal work-record UUIDs and external issue ids (design:
docs/uuid-external-id-mapping.md) - Two-way boundary sync when a third-party tracker is actually in use
- Zero load on the internal loop until an external integration is switched on
External trackers remain useful for:
- Human / open-source collaboration on GitHub, Forgejo, Jira, etc.
- Communication via comments when the counterparty lives in that UI
- Visibility for people who only watch the tracker, not the fleet hub
- Optional projection of a work record outward (not birth of the work record)
For agent coordination inside the Coulomb fleet, use work records + state-hub, not Forgejo as the default task board. See INTENT.md and SCOPE.md.
Current Status
Production-ready core with manual setup (v1.0)
✅ Fully Implemented:
- Complete CRUD operations (issues, labels, users, milestones, comments)
- Gitea backend (production-ready with full API integration)
- Local SQLite backend (offline work with sync capability)
- CLI with JSON output for machine parsing
- Python API for programmatic access
- Comprehensive filtering and search
- Basic synchronization between backends
⚠️ Current Limitations:
- Manual backend configuration required (one-time setup per project)
- No auto-detection from git remotes (coming in v1.1)
- Basic conflict resolution (manual intervention for complex cases)
- Hardcoded user context (agents need external identity management)
Quick Start
Installation
cd capabilities/issue-core
pip install -e . # CLI only
pip install -e ".[api]" # CLI + REST ingestion server
REST Server (intentional external issues)
issue-core exposes POST /issues/ for authenticated clients that deliberately
create external tracker issues. This is not the primary fleet path for
originating work (work records start as repo files). activity-core's
IssueSink may call this only when an external issue is intentional policy —
not as an always-on landing zone for internal findings.
export ISSUE_CORE_API_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"
issue serve --host 0.0.0.0 --port 8765
Clients authenticate with Authorization: Bearer <key> or X-API-Key: <key>.
See SCOPE.md "TaskSpec payload" for the request schema, or visit
http://<host>:<port>/docs once the server is running for live OpenAPI docs.
Configuration (One-Time Setup)
For Gitea-backed projects:
# Set your Gitea token
export GITEA_API_TOKEN="your-token-here"
# Configure backend
issue backend add myproject gitea
# Prompts for: URL, owner, repo (reads token from environment)
# Set as default
issue backend set-default myproject
# Verify
issue backend test myproject
For local/offline work:
issue backend add local-work local
# Prompts for: database path (.issue-core/issues.db)
issue backend set-default local-work
Basic Usage
# List issues (JSON output for agents)
issue list --format=json
# Create issue
issue create "Implement user authentication" \
--label=feature --label=priority:high
# Update state
issue edit 42 --state=in_progress --assignee=agent-coder
# Add comment
issue comment 42 "Implementation complete, tests passing"
# Close issue
issue close 42 --comment="Ready for review"
Agent Integration
Prefer work records + state-hub for fleet task lifecycle. Use issue-core when an agent must operate on an external tracker (project a task, update a Forgejo/GitHub issue, sync comments at the boundary).
See AGENT_INTEGRATION.md for:
- Programmatic Python API usage
- Multi-agent patterns against tracker backends (not as the fleet board)
- Label-based filters and state machine workflows on external issues
- Comment-based communication when the counterparty is in the tracker UI
- Workarounds for current limitations
- Performance optimization tips
Quick example:
from issue_core.backends.gitea import GiteaBackend
from issue_core.core.interfaces import IssueFilter
# Initialize
backend = GiteaBackend()
backend.connect(config)
# Query issues for agent
issues = backend.list_issues(IssueFilter(
state='open',
labels=['bug', 'priority:critical'],
assignee='agent-coder'
))
# Process each issue
for issue in issues:
# Agent implements fix
result = agent.fix_bug(issue)
# Report back
issue.state = IssueState.CLOSED
backend.update_issue(issue)
Architecture
Facade Pattern with Plugin Backends
┌─────────────────────────────────────┐
│ CLI Layer (Click) │
│ issue list | create | edit │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Core Domain Models │
│ (Issue, Label, User, etc.) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Backend Interface (ABC) │
│ IssueBackend, SyncableBackend │
└──────────────┬──────────────────────┘
│
┌───────┴────────┐
│ │
┌──────▼─────┐ ┌──────▼──────┐
│ Local │ │ Gitea │
│ (SQLite) │ │ (REST API) │
└────────────┘ └─────────────┘
Key Design Principles:
- Backend-agnostic core models
- Plugin architecture for easy backend addition
- Type-safe interfaces with comprehensive testing
- Sync support for offline/online workflows
Supported Backends
| Backend | Status | Features |
|---|---|---|
| Gitea | ✅ Production | Full API, rate limiting, state mapping |
| Local SQLite | ✅ Production | Offline work, fast queries, sync support |
| GitHub | 🚧 Planned (v1.1) | Full API integration |
| GitLab | 🚧 Planned (v1.2) | Full API integration |
Use Cases
1. External tracker ops (when a backend is in use)
Agents or humans work issues that already live in Gitea/GitHub (e.g. OSS inbound, customer Jira, or an intentional projection):
# List open external issues for a label
issue list --label=needs-implementation --format=json
# Claim and update state on the tracker
issue edit 42 --assignee=agent-coder --state=in_progress
issue comment 42 "Implementation complete, tests passing"
issue close 42 --comment="Ready for review"
Fleet-internal task claim/execute should use workplan task status in the repo
- state-hub, not Forgejo by default.
2. Human collaboration through a tracker UI
When the counterparty only uses GitHub/Gitea, project or update the external
issue and keep comments there. Mapping design (UUID ↔ external id) will tie
that projection back to the work record — see docs/uuid-external-id-mapping.md.
3. Offline development with sync
Work offline with local backend, sync when online:
# Setup local backup
issue backend add backup local
issue sync pull gitea-production backup
# Work offline
issue backend set-default backup
issue create "Offline tracker note" --label=offline
# Sync back
issue sync push backup gitea-production
CLI Commands Reference
Issue Operations
issue list [--state STATE] [--label LABEL] [--assignee USER] [--format FORMAT]
issue show ISSUE_NUMBER [--comments] [--format FORMAT]
issue create TITLE [--description DESC] [--label LABEL] [--assignee USER]
issue edit ISSUE_NUMBER [--title TITLE] [--state STATE] [--add-label LABEL]
issue close ISSUE_NUMBER [--comment COMMENT]
issue reopen ISSUE_NUMBER [--comment COMMENT]
issue comment ISSUE_NUMBER BODY
Backend Management
issue backend list
issue backend add NAME TYPE
issue backend remove NAME
issue backend test NAME
issue backend set-default NAME
Synchronization
issue sync status
issue sync pull SOURCE TARGET [--dry-run] [--force]
issue sync push SOURCE TARGET
issue sync bidirectional BACKEND1 BACKEND2
Development
Testing
# Install with dev dependencies
make install-dev
# Run all tests (109 tests, 61% coverage)
make test
# Run with coverage report
make test-cov
# Run only unit tests
make test-unit
Code Quality
# Run linter
make issue-core-lint
# Format code
black issue_core/ tests/
# Type check
mypy issue_core/
Project Structure
issue-core/
├── issue_core/
│ ├── core/ # Domain models and interfaces
│ │ ├── models.py # Issue, Label, User, etc.
│ │ └── interfaces.py # IssueBackend, SyncableBackend
│ ├── backends/
│ │ ├── gitea/ # Gitea backend implementation
│ │ └── local/ # SQLite backend implementation
│ └── cli/ # Click-based CLI
│ ├── commands.py # Issue operations
│ ├── backend_commands.py
│ └── sync_commands.py
├── tests/ # 109 tests, comprehensive coverage
├── examples/ # Agent integration examples
├── AGENT_INTEGRATION.md # Agent coordination guide
├── CLAUDE.md # Development guide for Claude Code
└── ROADMAP.md # Future enhancements
Documentation
- INTENT.md — why issue-core exists (work-record-aligned connector)
- SCOPE.md — shipped inventory and product boundary
- docs/uuid-external-id-mapping.md — mapping design
- docs/boundary-sync-and-status-mapping.md — dual-lifecycle policy
- AGENT_INTEGRATION.md — programmatic API for tracker backends
- ROADMAP.md — feature trajectory (connector-aligned)
- Work-record canon:
the-custodian/canon/standards/work-record-types_v0.1.md - Architecture §4.2:
the-custodian/research/WorkOrchestrationArchitectureDraft.md
Work-record projection (mapping)
# Project a work record to the default backend (idempotent)
issue project <work-record-uuid> --title "External title" --canonical-id ISSUE-WP-0005-T05
# Link an existing tracker issue
issue map link <work-record-uuid> 42
# Resolve / detach / outward status push
issue map show --uuid <work-record-uuid>
issue map push-status --uuid <work-record-uuid> --status progress
issue map detach --uuid <work-record-uuid>
Mappings live in ~/.config/issue-tracker/mappings.db (bookkeeping), independent
of whether CRUD targets Gitea or local SQLite.
Roadmap (summary)
Connector alignment (docs — ISSUE-WP-0004)
- INTENT / SCOPE / README framing as external connector
- UUID ↔ external-id mapping design
v1.1 - Auto-Configuration
- Automatic git remote detection
- Environment-variable-only setup
- Per-repository configuration files
issue config detectcommand
Mapping + boundary sync (stage-3 work-record architecture)
- Persist work-record UUID ↔ backend issue id
- Optional two-way sync when a tracker is switched on
- No load when integrations are off
Further backends / agent helpers
- GitHub, GitLab, Jira backends
- Claiming helpers for external issues
- Webhooks for tracker-side events
Comparison with Platform CLIs
| Feature | Issue Core | gh (GitHub) | glab (GitLab) |
|---|---|---|---|
| Multi-backend support | ✅ Yes | ❌ GitHub only | ❌ GitLab only |
| Offline capability | ✅ Local SQLite | ❌ No | ❌ No |
| Agent-friendly API | ✅ Python + JSON | ⚠️ CLI only | ⚠️ CLI only |
| Consistent interface | ✅ Same across all | ❌ Platform-specific | ❌ Platform-specific |
| Backend sync | ✅ Yes | ❌ No | ❌ No |
| Auto-configuration | 🚧 Coming v1.1 | ✅ Yes | ✅ Yes |
Contributing
The Issue Core is designed to be extensible:
To add a new backend:
- Implement the
IssueBackendinterface (seecore/interfaces.py) - Handle platform-specific API details in your backend
- Map platform models to/from core domain models
- Add comprehensive tests
- Register in
BackendFactory
Backend implementation checklist:
- All CRUD operations (issues, labels, users, milestones, comments)
- State mapping to/from platform-specific states
- Error handling and rate limiting
- Sync support (if applicable)
- Integration tests with mock API
- Documentation
See existing backends (Gitea, Local) as reference implementations.
Why "Facade" / connector?
The Facade Pattern still describes the implementation style:
"Provide a unified interface to a set of interfaces in a subsystem." — Gang of Four, Design Patterns
Instead of agents learning different APIs for GitHub (gh), GitLab (glab), Gitea, JIRA, etc., they use one consistent interface. The facade does not replace issue trackers — and it does not replace work records as the origin of fleet work. It connects the fleet to trackers at the boundary.
License
MIT License - See LICENSE file