generated from coulomb/repo-seed
Renames the package, distribution, CLI alias, Makefile targets, and working directory from issue-facade to issue-core, signalling its role as the authoritative task lifecycle manager for the Coulomb org (peer to activity-core, rules-core, project-core). Adds POST /issues/ ingestion endpoint for activity-core's IssueSink, under a new optional [api] extra. The endpoint is served by `issue serve`, authenticates via the ISSUE_CORE_API_KEY env var (Bearer or X-API-Key header), and routes the TaskSpec payload to the configured default backend with full traceability metadata embedded in sync_metadata. - T01: Python package issue_tracker -> issue_core, dir rename - T02: registered in state hub under custodian domain - T03: INTENT.md (what it is, what it isn't, how it fits) - T04: SCOPE.md (in/out-of-scope, integration boundaries) - T05: POST /issues/ via FastAPI + Uvicorn, 9 unit tests - T06: docs/nats-task-ingestion.md design stub Closes ISSC-WP-0001. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
324 lines
9.1 KiB
Markdown
324 lines
9.1 KiB
Markdown
# Issue Core - Agent Integration Context
|
|
|
|
**🤖 For Coding Agents: Read this to understand how to use issue tracking in this project.**
|
|
|
|
## Critical: DO NOT Bypass This Capability
|
|
|
|
⚠️ **IMPORTANT:** If you need to work with issues, **you MUST use this capability**. Do NOT:
|
|
- ❌ Make direct API calls to Gitea/GitHub/GitLab
|
|
- ❌ Use platform CLIs (gh, glab, etc.)
|
|
- ❌ Import platform libraries (PyGithub, python-gitlab, etc.)
|
|
- ❌ Parse HTML/scrape issue tracker web UIs
|
|
|
|
**Why?** Bypassing this capability causes:
|
|
- Credential management chaos (tokens scattered everywhere)
|
|
- Inconsistent issue state across agents
|
|
- Massive token waste (redundant API calls)
|
|
- Platform lock-in (can't switch Gitea → GitHub easily)
|
|
- Race conditions in multi-agent scenarios
|
|
|
|
## Quick Reference
|
|
|
|
### Check if Capability is Available
|
|
|
|
```bash
|
|
# Verify installation
|
|
issue --version
|
|
# or
|
|
python -c "from issue_core.backends.gitea import GiteaBackend; print('OK')"
|
|
```
|
|
|
|
### Basic Usage (Python)
|
|
|
|
```python
|
|
from issue_core.backends.gitea import GiteaBackend
|
|
from issue_core.core.models import Issue, Label, IssueState, User, Comment
|
|
from issue_core.core.interfaces import IssueFilter
|
|
from datetime import datetime, timezone
|
|
import os
|
|
|
|
# Connect (assumes backend is configured)
|
|
backend = GiteaBackend()
|
|
backend.connect({
|
|
'base_url': os.environ['GITEA_URL'],
|
|
'token': os.environ['GITEA_API_TOKEN'],
|
|
'owner': os.environ['GITEA_OWNER'],
|
|
'repo': os.environ['GITEA_REPO']
|
|
})
|
|
|
|
# List issues for me
|
|
my_issues = backend.list_issues(IssueFilter(
|
|
state='open',
|
|
assignee='my-agent-id',
|
|
labels=['needs-implementation']
|
|
))
|
|
|
|
# Create issue
|
|
new_issue = Issue(
|
|
id=None, number=0,
|
|
title="Implement feature X",
|
|
description="Details...",
|
|
state=IssueState.OPEN,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
labels=[Label(name="feature"), Label(name="priority:high")]
|
|
)
|
|
created = backend.create_issue(new_issue)
|
|
|
|
# Update issue
|
|
created.state = IssueState.IN_PROGRESS
|
|
created.assignees = [User(id="agent-id", username="agent-id")]
|
|
backend.update_issue(created)
|
|
|
|
# Add comment
|
|
comment = Comment(
|
|
id=None,
|
|
body="Implementation started. Working on database schema.",
|
|
author=User(id="agent-id", username="agent-id"),
|
|
created_at=datetime.now(timezone.utc)
|
|
)
|
|
backend.add_comment(created.id, comment)
|
|
|
|
# Close when done
|
|
created.state = IssueState.CLOSED
|
|
created.closed_at = datetime.now(timezone.utc)
|
|
backend.update_issue(created)
|
|
```
|
|
|
|
### Basic Usage (CLI)
|
|
|
|
```bash
|
|
# List my open issues
|
|
issue list --state=open --assignee=agent-id --format=json
|
|
|
|
# Create issue
|
|
issue create "Implement feature X" \
|
|
--label=feature \
|
|
--label=priority:high \
|
|
--description="Details here"
|
|
|
|
# Update state
|
|
issue edit 42 --state=in_progress --assignee=agent-id
|
|
|
|
# Add comment
|
|
issue comment 42 "Implementation started"
|
|
|
|
# Close
|
|
issue close 42 --comment="Completed successfully"
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Pattern 1: Find Work
|
|
|
|
```python
|
|
# Get next available task
|
|
available_tasks = backend.list_issues(IssueFilter(
|
|
state='open',
|
|
labels=['ready', 'needs-implementation']
|
|
))
|
|
|
|
# Filter to unassigned
|
|
unassigned = [t for t in available_tasks if not t.assignees]
|
|
|
|
if unassigned:
|
|
task = unassigned[0]
|
|
# Claim it...
|
|
```
|
|
|
|
### Pattern 2: Claim Issue (Prevent Race Conditions)
|
|
|
|
```python
|
|
def claim_issue(issue: Issue, agent_id: str) -> bool:
|
|
"""Claim an issue safely."""
|
|
# Check if already claimed
|
|
if issue.assignees:
|
|
return False # Already taken
|
|
|
|
# Claim it
|
|
issue.state = IssueState.IN_PROGRESS
|
|
issue.assignees = [User(id=agent_id, username=agent_id)]
|
|
backend.update_issue(issue)
|
|
|
|
# Announce claim
|
|
backend.add_comment(issue.id, Comment(
|
|
id=None,
|
|
body=f"🤖 Claimed by {agent_id}",
|
|
author=User(id=agent_id, username=agent_id),
|
|
created_at=datetime.now(timezone.utc)
|
|
))
|
|
return True
|
|
```
|
|
|
|
### Pattern 3: Progress Updates
|
|
|
|
```python
|
|
def report_progress(issue: Issue, message: str, agent_id: str):
|
|
"""Report progress on an issue."""
|
|
backend.add_comment(issue.id, Comment(
|
|
id=None,
|
|
body=f"**Progress Update:**\n\n{message}",
|
|
author=User(id=agent_id, username=agent_id),
|
|
created_at=datetime.now(timezone.utc)
|
|
))
|
|
```
|
|
|
|
### Pattern 4: Agent-to-Agent Communication
|
|
|
|
```python
|
|
import json
|
|
|
|
def post_agent_message(issue_id: str, msg_type: str, data: dict, agent_id: str):
|
|
"""Post structured message for other agents."""
|
|
message = {
|
|
'type': msg_type,
|
|
'agent': agent_id,
|
|
'timestamp': datetime.now(timezone.utc).isoformat(),
|
|
'data': data
|
|
}
|
|
backend.add_comment(issue_id, Comment(
|
|
id=None,
|
|
body=f"```agent-message\n{json.dumps(message, indent=2)}\n```",
|
|
author=User(id=agent_id, username=agent_id),
|
|
created_at=datetime.now(timezone.utc)
|
|
))
|
|
|
|
def read_agent_messages(issue_id: str, msg_type: str = None):
|
|
"""Read messages from other agents."""
|
|
comments = backend.get_comments(issue_id)
|
|
messages = []
|
|
for comment in comments:
|
|
if '```agent-message' in comment.body:
|
|
try:
|
|
json_str = comment.body.split('```agent-message\n')[1].split('\n```')[0]
|
|
msg = json.loads(json_str)
|
|
if msg_type is None or msg['type'] == msg_type:
|
|
messages.append(msg)
|
|
except:
|
|
continue
|
|
return messages
|
|
```
|
|
|
|
## Configuration Check
|
|
|
|
Before using issue tracking, verify configuration:
|
|
|
|
```python
|
|
def verify_issue_backend() -> bool:
|
|
"""Verify issue backend is configured."""
|
|
try:
|
|
backend = GiteaBackend()
|
|
backend.connect({
|
|
'base_url': os.environ['GITEA_URL'],
|
|
'token': os.environ['GITEA_API_TOKEN'],
|
|
'owner': os.environ['GITEA_OWNER'],
|
|
'repo': os.environ['GITEA_REPO']
|
|
})
|
|
return backend.test_connection()
|
|
except Exception as e:
|
|
print(f"Issue backend not configured: {e}")
|
|
return False
|
|
|
|
# Use it
|
|
if not verify_issue_backend():
|
|
print("ERROR: Issue tracking not available. Check configuration.")
|
|
sys.exit(1)
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
```python
|
|
from issue_core.backends.gitea.backend import GiteaAPIError
|
|
|
|
try:
|
|
issue = backend.get_issue_by_number(42)
|
|
except GiteaAPIError as e:
|
|
if e.status_code == 404:
|
|
print("Issue not found")
|
|
elif e.status_code == 401:
|
|
print("Authentication failed - check GITEA_API_TOKEN")
|
|
elif e.status_code == 429:
|
|
print("Rate limited - wait and retry")
|
|
else:
|
|
print(f"API error: {e}")
|
|
```
|
|
|
|
## Performance Tips
|
|
|
|
1. **Use filters** instead of fetching all issues:
|
|
```python
|
|
# BAD: Get all, filter in Python
|
|
all_issues = backend.list_issues()
|
|
my_issues = [i for i in all_issues if i.assignees and i.assignees[0].username == 'me']
|
|
|
|
# GOOD: Filter at backend
|
|
my_issues = backend.list_issues(IssueFilter(assignee='me'))
|
|
```
|
|
|
|
2. **Use JSON output** for CLI parsing:
|
|
```bash
|
|
issue list --format=json | jq '.[] | select(.state == "open")'
|
|
```
|
|
|
|
3. **Batch comments** instead of rapid-fire updates
|
|
|
|
4. **Check local cache** before querying (if available)
|
|
|
|
## Troubleshooting
|
|
|
|
### "Backend not configured"
|
|
```bash
|
|
# Check config
|
|
issue backend list
|
|
|
|
# If empty, configure
|
|
export GITEA_API_TOKEN="your-token"
|
|
issue backend add myproject gitea
|
|
issue backend set-default myproject
|
|
```
|
|
|
|
### "Authentication failed"
|
|
```bash
|
|
# Verify token
|
|
curl -H "Authorization: token $GITEA_API_TOKEN" $GITEA_URL/api/v1/user
|
|
```
|
|
|
|
### "Issue not found"
|
|
```python
|
|
# Use get_issue_by_number, not get_issue
|
|
issue = backend.get_issue_by_number(42) # Correct
|
|
# issue = backend.get_issue("42") # Wrong - needs backend_id
|
|
```
|
|
|
|
## Full Documentation
|
|
|
|
- **Integration Guide:** `AGENT_INTEGRATION.md` (comprehensive patterns and strategies)
|
|
- **API Reference:** `CLAUDE.md` (for developers extending the capability)
|
|
- **Examples:** `examples/agents/` (working agent implementations)
|
|
- **Roadmap:** `ROADMAP.md` (upcoming features)
|
|
|
|
## Current Limitations (v1.0)
|
|
|
|
Be aware of these limitations:
|
|
|
|
1. **Manual Configuration:** Backend must be configured before use (auto-detect in v1.1)
|
|
2. **User Context:** Uses hardcoded user for CLI operations (agent identity in v1.2)
|
|
3. **No Built-in Locking:** Use assignee + comment workaround for claiming (native in v1.2)
|
|
4. **Basic Conflicts:** Manual resolution required for complex sync conflicts (advanced in v2.0)
|
|
|
|
Workarounds are documented in `AGENT_INTEGRATION.md`.
|
|
|
|
## Questions?
|
|
|
|
If you're unsure whether to use this capability for something:
|
|
|
|
**ASK:** "Does this involve creating, reading, updating, or searching issues?"
|
|
- **YES** → Use this capability
|
|
- **NO** → You can use other methods
|
|
|
|
**Example:**
|
|
- "Create an issue for the bug I found" → **Use issue-core**
|
|
- "Read the project README" → Don't need issue-core
|
|
- "Check if issue #42 exists" → **Use issue-core**
|
|
- "Clone the repository" → Don't need issue-core
|