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.
428 lines
15 KiB
Markdown
428 lines
15 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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:**
|
|
|
|
```bash
|
|
# 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:**
|
|
|
|
```bash
|
|
issue backend add local-work local
|
|
# Prompts for: database path (.issue-core/issues.db)
|
|
|
|
issue backend set-default local-work
|
|
```
|
|
|
|
### Basic Usage
|
|
|
|
```bash
|
|
# 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](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:
|
|
|
|
```python
|
|
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):
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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
|
|
```bash
|
|
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
|
|
```bash
|
|
issue backend list
|
|
issue backend add NAME TYPE
|
|
issue backend remove NAME
|
|
issue backend test NAME
|
|
issue backend set-default NAME
|
|
```
|
|
|
|
### Synchronization
|
|
```bash
|
|
issue sync status
|
|
issue sync pull SOURCE TARGET [--dry-run] [--force]
|
|
issue sync push SOURCE TARGET
|
|
issue sync bidirectional BACKEND1 BACKEND2
|
|
```
|
|
|
|
## Development
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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](INTENT.md)** — why issue-core exists (work-record-aligned connector)
|
|
- **[SCOPE.md](SCOPE.md)** — shipped inventory and product boundary
|
|
- **[docs/uuid-external-id-mapping.md](docs/uuid-external-id-mapping.md)** — mapping design
|
|
- **[docs/boundary-sync-and-status-mapping.md](docs/boundary-sync-and-status-mapping.md)** — dual-lifecycle policy
|
|
- **[AGENT_INTEGRATION.md](AGENT_INTEGRATION.md)** — programmatic API for tracker backends
|
|
- **[ROADMAP.md](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)
|
|
|
|
```bash
|
|
# 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 detect` command
|
|
|
|
### 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:**
|
|
1. Implement the `IssueBackend` interface (see `core/interfaces.py`)
|
|
2. Handle platform-specific API details in your backend
|
|
3. Map platform models to/from core domain models
|
|
4. Add comprehensive tests
|
|
5. 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
|