2025-12-17 19:32:37 +01:00
|
|
|
# Agent Integration Guide
|
|
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
**Issue Core for external tracker projection** — not the fleet ops queue.
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
## Purpose
|
|
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
The **Issue Core** capability provides a standardized interface for autonomous
|
|
|
|
|
coding agents to operate on **external issue trackers** (Forgejo, GitHub,
|
|
|
|
|
GitLab, Jira). Instead of agents learning platform-specific APIs, they use a
|
|
|
|
|
unified abstraction. The self-hosted forge is **Forgejo** only; the HTTP API
|
|
|
|
|
is Gitea-compatible (`issue_core.backends.gitea`, backend type `gitea`).
|
2026-07-21 04:22:12 +02:00
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
**Fleet note (2026-07-20 / ISSUE-WP-0006):** Internal work originates as repo
|
|
|
|
|
work records (ADR-001 / work-record canon), not as issue-core issues. Use this
|
|
|
|
|
guide when an agent must project to or work inside a third-party tracker. See
|
|
|
|
|
`INTENT.md` and `docs/uuid-external-id-mapping.md`.
|
|
|
|
|
|
|
|
|
|
### Happy path (projection, not automation loop)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
1. Author work record in repo (ADR-001)
|
|
|
|
|
│
|
|
|
|
|
│ fix-consistency
|
|
|
|
|
v
|
|
|
|
|
state-hub index (UUIDv7 write-back)
|
|
|
|
|
│
|
|
|
|
|
│ optional project / link
|
|
|
|
|
v
|
|
|
|
|
2. issue-core ──► Forgejo (or GitHub / Jira later)
|
|
|
|
|
3. mapping: work-record UUID ↔ (backend=forgejo, external_id)
|
|
|
|
|
stored backend type string is currently `gitea`
|
|
|
|
|
(Forgejo Gitea-compatible API)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Internal scheduled automation (FI/Binky, mail, …) does **not** follow
|
|
|
|
|
`activity-core → issue-core → harness`. That path is **legacy / external
|
|
|
|
|
tickets only**. Claim/execute for fleet ops is activity-core **`ops_run`**
|
|
|
|
|
(ACT-ADR-005). See rein-aharness `docs/task-intake.md` and ISSUE-WP-0006.
|
2025-12-17 19:32:37 +01:00
|
|
|
|
2026-07-22 00:25:06 +02:00
|
|
|
### When to use a tracker (and when not to)
|
2025-12-17 19:32:37 +01:00
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
**Fleet claim/execute** runs on work records (repo files + state-hub) or on
|
|
|
|
|
`ops_run` for scheduled automation — not on Forgejo by default. Use
|
|
|
|
|
issue-core when:
|
2025-12-17 19:32:37 +01:00
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
- A counterparty or OSS workflow lives in Forgejo/GitHub/Jira
|
2026-07-22 00:25:06 +02:00
|
|
|
- You need to **project or link** a work record (`issue project` / `issue map`)
|
|
|
|
|
- You must update or comment on an **external** issue already in a tracker
|
|
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
Do **not** treat issue-core as the org task board, the ops claim queue, or
|
|
|
|
|
the origin of intake/tasks. Findings → `kind: intake` + promotion; optional
|
|
|
|
|
later projection.
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
## Current Status: Production-Ready with Manual Setup
|
|
|
|
|
|
|
|
|
|
### What Works Now (v1.0)
|
|
|
|
|
|
|
|
|
|
✅ **Complete CRUD Operations**
|
|
|
|
|
- Create, read, update, delete issues
|
|
|
|
|
- Full label management
|
|
|
|
|
- User and assignee handling
|
|
|
|
|
- Milestone operations
|
|
|
|
|
- Comment threads
|
|
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
✅ **Forgejo Backend** (Production-Ready; Gitea-compatible API)
|
2025-12-17 19:32:37 +01:00
|
|
|
- Complete API integration
|
|
|
|
|
- Rate limiting and error handling
|
|
|
|
|
- State mapping (open/in_progress/blocked → open/closed)
|
|
|
|
|
- Sync support with local backup
|
|
|
|
|
|
|
|
|
|
✅ **Local SQLite Backend** (Fully Functional)
|
|
|
|
|
- Offline operation
|
|
|
|
|
- Complete data model
|
|
|
|
|
- Sync with remote backends
|
|
|
|
|
- Fast queries and filtering
|
|
|
|
|
|
|
|
|
|
✅ **Agent-Friendly Features**
|
|
|
|
|
- JSON output mode for machine parsing
|
|
|
|
|
- Programmatic Python API
|
|
|
|
|
- Comprehensive filtering
|
|
|
|
|
- Batch operations
|
|
|
|
|
- Type-safe models
|
|
|
|
|
|
|
|
|
|
### Current Limitations
|
|
|
|
|
|
|
|
|
|
⚠️ **Manual Configuration Required**
|
|
|
|
|
- No auto-detection from git remotes (yet)
|
|
|
|
|
- Backend configuration is manual one-time setup
|
|
|
|
|
- No environment-variable-only mode (yet)
|
|
|
|
|
|
|
|
|
|
⚠️ **Hardcoded User Context**
|
|
|
|
|
- CLI operations use "cli-user" placeholder
|
|
|
|
|
- Agent identity needs to be managed externally
|
|
|
|
|
|
|
|
|
|
⚠️ **Basic Conflict Resolution**
|
|
|
|
|
- Sync detects conflicts but doesn't auto-resolve
|
|
|
|
|
- Manual intervention required for complex merges
|
|
|
|
|
|
|
|
|
|
## Quick Start for Agents
|
|
|
|
|
|
|
|
|
|
### 1. Installation
|
|
|
|
|
|
|
|
|
|
```bash
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
cd capabilities/issue-core
|
2025-12-17 19:32:37 +01:00
|
|
|
pip install -e .
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Backend Configuration (One-Time Setup)
|
|
|
|
|
|
2026-09-14 04:52:58 +02:00
|
|
|
**For Forgejo Projects:**
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
```bash
|
2026-09-14 04:52:58 +02:00
|
|
|
# Configure Forgejo backend (type string `gitea` = Gitea-compatible API)
|
|
|
|
|
export FORGEJO_TOKEN="your-token-here"
|
|
|
|
|
# Deprecated aliases: FORGEJO_API_TOKEN, GITEA_API_TOKEN, GITEA_TOKEN
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
issue backend add my-project gitea
|
|
|
|
|
# Prompts for:
|
2026-09-14 04:52:58 +02:00
|
|
|
# - Forgejo URL: https://forgejo.example.com
|
2025-12-17 19:32:37 +01:00
|
|
|
# - Owner: your-org
|
|
|
|
|
# - Repo: your-project
|
2026-09-14 04:52:58 +02:00
|
|
|
# - Token: (reads FORGEJO_TOKEN, then deprecated GITEA_* aliases)
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
# Verify connection
|
|
|
|
|
issue backend test my-project
|
|
|
|
|
|
|
|
|
|
# Set as default
|
|
|
|
|
issue backend set-default my-project
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**For Local/Offline Work:**
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Configure local SQLite backend
|
|
|
|
|
issue backend add local-work local
|
|
|
|
|
# Prompts for:
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
# - Database path: .issue-core/issues.db
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
issue backend set-default local-work
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. Basic Agent Operations
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# List open issues assigned to an agent
|
|
|
|
|
issue list --state=open --assignee=agent-coder --format=json
|
|
|
|
|
|
|
|
|
|
# Create a new issue
|
|
|
|
|
issue create "Implement user authentication" \
|
|
|
|
|
--label=feature --label=priority:high \
|
|
|
|
|
--assignee=agent-coder
|
|
|
|
|
|
|
|
|
|
# Update issue state
|
|
|
|
|
issue edit 42 --state=in_progress
|
|
|
|
|
|
|
|
|
|
# Add progress comment
|
|
|
|
|
issue comment 42 "Completed database schema migration"
|
|
|
|
|
|
|
|
|
|
# Close when done
|
|
|
|
|
issue close 42 --comment="Implementation complete, tests passing"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Agent Workflow Patterns
|
|
|
|
|
|
|
|
|
|
### Pattern 1: Single Agent, Task Execution
|
|
|
|
|
|
|
|
|
|
**Scenario**: One agent implements features from an issue backlog.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
# Agent workflow script
|
|
|
|
|
|
|
|
|
|
# 1. Get next available task
|
|
|
|
|
ISSUE=$(issue list --state=open --label=ready \
|
|
|
|
|
--format=json --limit=1 | jq -r '.[0].number')
|
|
|
|
|
|
|
|
|
|
# 2. Claim the issue
|
|
|
|
|
issue edit $ISSUE --assignee=agent-coder --state=in_progress
|
|
|
|
|
issue comment $ISSUE "Starting implementation"
|
|
|
|
|
|
|
|
|
|
# 3. Execute work
|
|
|
|
|
# ... agent implements the feature ...
|
|
|
|
|
|
|
|
|
|
# 4. Report completion
|
|
|
|
|
issue comment $ISSUE "Implementation complete. Files changed: src/auth.py, tests/test_auth.py"
|
|
|
|
|
issue close $ISSUE --comment="Ready for review"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Pattern 2: Multi-Agent Coordination
|
|
|
|
|
|
|
|
|
|
**Scenario**: Multiple specialized agents work on different aspects.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Agent 1 (Coder) - Claims and implements
|
|
|
|
|
issue list --label=needs-implementation --state=open --format=json | \
|
|
|
|
|
jq -r '.[0].number' | \
|
|
|
|
|
xargs -I {} issue edit {} --assignee=agent-coder --state=in_progress
|
|
|
|
|
|
|
|
|
|
# Agent 2 (Reviewer) - Reviews completed work
|
|
|
|
|
issue list --label=needs-review --state=closed --format=json | \
|
|
|
|
|
jq -r '.[0].number' | \
|
|
|
|
|
xargs -I {} sh -c 'issue comment {} "Code review complete. Approved."'
|
|
|
|
|
|
|
|
|
|
# Agent 3 (Tester) - Runs tests on reviewed code
|
|
|
|
|
issue list --label=reviewed --state=closed --format=json | \
|
|
|
|
|
jq -r '.[0].number' | \
|
|
|
|
|
xargs -I {} sh -c 'issue comment {} "All tests passing. Deploying to staging."'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Pattern 3: Agent-Human Collaboration
|
|
|
|
|
|
|
|
|
|
**Scenario**: Agents implement, humans review and approve.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Agent creates implementation issues from requirements
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
from issue_core.backends.gitea import GiteaBackend
|
|
|
|
|
from issue_core.core.models import Issue, Label, IssueState
|
2025-12-17 19:32:37 +01:00
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
|
|
|
|
backend = GiteaBackend()
|
|
|
|
|
backend.connect({
|
2026-09-14 04:52:58 +02:00
|
|
|
'base_url': os.environ.get('FORGEJO_URL', 'https://forgejo.example.com'),
|
|
|
|
|
'token': os.environ.get('FORGEJO_TOKEN') or os.environ['GITEA_API_TOKEN'],
|
2025-12-17 19:32:37 +01:00
|
|
|
'owner': 'myorg',
|
|
|
|
|
'repo': 'myproject'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Agent breaks down feature into tasks
|
|
|
|
|
feature_issue = backend.get_issue_by_number(100)
|
|
|
|
|
subtasks = [
|
|
|
|
|
"Implement database schema",
|
|
|
|
|
"Create API endpoints",
|
|
|
|
|
"Add frontend components",
|
|
|
|
|
"Write integration tests"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for task in subtasks:
|
|
|
|
|
issue = Issue(
|
|
|
|
|
id=None, number=0,
|
|
|
|
|
title=f"{feature_issue.title}: {task}",
|
|
|
|
|
description=f"Subtask of #{feature_issue.number}\n\n{task}",
|
|
|
|
|
state=IssueState.OPEN,
|
|
|
|
|
created_at=datetime.now(timezone.utc),
|
|
|
|
|
updated_at=datetime.now(timezone.utc),
|
|
|
|
|
labels=[
|
|
|
|
|
Label(name="agent-generated"),
|
|
|
|
|
Label(name="needs-implementation"),
|
|
|
|
|
Label(name="parent:100")
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
backend.create_issue(issue)
|
|
|
|
|
|
|
|
|
|
# Human reviews and approves/rejects via comments
|
|
|
|
|
# Agent monitors for approval comments and proceeds
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Programmatic API for Agents
|
|
|
|
|
|
|
|
|
|
### Python Integration
|
|
|
|
|
|
|
|
|
|
```python
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
from issue_core.backends.gitea import GiteaBackend
|
|
|
|
|
from issue_core.core.models import Issue, Label, IssueState, User
|
|
|
|
|
from issue_core.core.interfaces import IssueFilter
|
2025-12-17 19:32:37 +01:00
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
# Initialize backend
|
|
|
|
|
backend = GiteaBackend()
|
|
|
|
|
backend.connect({
|
2026-09-14 04:52:58 +02:00
|
|
|
'base_url': os.environ.get('FORGEJO_URL') or os.environ['GITEA_URL'],
|
|
|
|
|
'token': os.environ.get('FORGEJO_TOKEN') or os.environ.get('FORGEJO_API_TOKEN') or os.environ['GITEA_API_TOKEN'],
|
|
|
|
|
'owner': os.environ.get('FORGEJO_OWNER') or os.environ['GITEA_OWNER'],
|
|
|
|
|
'repo': os.environ.get('FORGEJO_REPO') or os.environ['GITEA_REPO']
|
2025-12-17 19:32:37 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Query issues
|
|
|
|
|
filter_criteria = IssueFilter(
|
|
|
|
|
state='open',
|
|
|
|
|
labels=['bug', 'priority:high'],
|
|
|
|
|
assignee='agent-coder',
|
|
|
|
|
limit=10
|
|
|
|
|
)
|
|
|
|
|
issues = backend.list_issues(filter_criteria)
|
|
|
|
|
|
|
|
|
|
# Create issue
|
|
|
|
|
new_issue = Issue(
|
|
|
|
|
id=None,
|
|
|
|
|
number=0,
|
|
|
|
|
title="Fix memory leak in parser",
|
|
|
|
|
description="Detected memory leak in parse_document() function",
|
|
|
|
|
state=IssueState.OPEN,
|
|
|
|
|
created_at=datetime.now(timezone.utc),
|
|
|
|
|
updated_at=datetime.now(timezone.utc),
|
|
|
|
|
labels=[
|
|
|
|
|
Label(name="bug"),
|
|
|
|
|
Label(name="priority:critical"),
|
|
|
|
|
Label(name="agent-detected")
|
|
|
|
|
],
|
|
|
|
|
assignees=[User(id="agent-coder", username="agent-coder")]
|
|
|
|
|
)
|
|
|
|
|
created = backend.create_issue(new_issue)
|
|
|
|
|
|
|
|
|
|
# Update issue
|
|
|
|
|
created.state = IssueState.IN_PROGRESS
|
|
|
|
|
backend.update_issue(created)
|
|
|
|
|
|
|
|
|
|
# Add comment
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
from issue_core.core.models import Comment
|
2025-12-17 19:32:37 +01:00
|
|
|
comment = Comment(
|
|
|
|
|
id=None,
|
|
|
|
|
body="Analysis complete. Root cause: unclosed file handles in line 234",
|
|
|
|
|
author=User(id="agent-coder", username="agent-coder"),
|
|
|
|
|
created_at=datetime.now(timezone.utc)
|
|
|
|
|
)
|
|
|
|
|
backend.add_comment(created.id, comment)
|
|
|
|
|
|
|
|
|
|
# Close issue
|
|
|
|
|
created.state = IssueState.CLOSED
|
|
|
|
|
created.closed_at = datetime.now(timezone.utc)
|
|
|
|
|
backend.update_issue(created)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Advanced Filtering
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Get all high-priority bugs not assigned
|
|
|
|
|
critical_bugs = backend.list_issues(IssueFilter(
|
|
|
|
|
state='open',
|
|
|
|
|
labels=['bug', 'priority:critical']
|
|
|
|
|
))
|
|
|
|
|
unassigned = [i for i in critical_bugs if not i.assignees]
|
|
|
|
|
|
|
|
|
|
# Get stale issues (not updated in 7 days)
|
|
|
|
|
from datetime import timedelta
|
|
|
|
|
stale_threshold = datetime.now(timezone.utc) - timedelta(days=7)
|
|
|
|
|
stale_issues = backend.list_issues(IssueFilter(
|
|
|
|
|
state='open',
|
|
|
|
|
updated_before=stale_threshold
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
# Search by text
|
|
|
|
|
search_results = backend.search_issues("authentication", limit=20)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Agent Coordination Strategies
|
|
|
|
|
|
|
|
|
|
### Strategy 1: Label-Based Role Assignment
|
|
|
|
|
|
|
|
|
|
Use labels to indicate agent specialization:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Agent types
|
|
|
|
|
AGENT_ROLES = {
|
|
|
|
|
'agent:coder': ['feature', 'bug', 'refactor'],
|
|
|
|
|
'agent:tester': ['needs-testing', 'test-failure'],
|
|
|
|
|
'agent:reviewer': ['needs-review', 'code-quality'],
|
|
|
|
|
'agent:documenter': ['documentation', 'api-docs']
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Each agent filters by their role
|
|
|
|
|
def get_agent_tasks(agent_type):
|
|
|
|
|
role_labels = AGENT_ROLES[agent_type]
|
|
|
|
|
all_tasks = []
|
|
|
|
|
for label in role_labels:
|
|
|
|
|
tasks = backend.list_issues(IssueFilter(
|
|
|
|
|
state='open',
|
|
|
|
|
labels=[label, agent_type]
|
|
|
|
|
))
|
|
|
|
|
all_tasks.extend(tasks)
|
|
|
|
|
return all_tasks
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Strategy 2: State Machine Workflow
|
|
|
|
|
|
|
|
|
|
Use issue states to track progress through pipeline:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
open → in_progress → needs_review → closed
|
|
|
|
|
↓ ↓ ↓
|
|
|
|
|
blocked blocked blocked
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
def advance_issue_state(issue_number):
|
|
|
|
|
issue = backend.get_issue_by_number(issue_number)
|
|
|
|
|
|
|
|
|
|
state_transitions = {
|
|
|
|
|
IssueState.OPEN: IssueState.IN_PROGRESS,
|
|
|
|
|
IssueState.IN_PROGRESS: IssueState.CLOSED # or needs_review
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if issue.state in state_transitions:
|
|
|
|
|
issue.state = state_transitions[issue.state]
|
|
|
|
|
backend.update_issue(issue)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Strategy 3: Comment-Based Communication
|
|
|
|
|
|
|
|
|
|
Use structured comments for agent-to-agent messages:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
def post_agent_message(issue_id, message_type, data):
|
|
|
|
|
"""Post structured message for other agents"""
|
|
|
|
|
message = {
|
|
|
|
|
'type': message_type,
|
|
|
|
|
'agent': 'agent-coder',
|
|
|
|
|
'timestamp': datetime.now(timezone.utc).isoformat(),
|
|
|
|
|
'data': data
|
|
|
|
|
}
|
|
|
|
|
comment = Comment(
|
|
|
|
|
id=None,
|
|
|
|
|
body=f"```agent-message\n{json.dumps(message, indent=2)}\n```",
|
|
|
|
|
author=User(id="agent-coder", username="agent-coder"),
|
|
|
|
|
created_at=datetime.now(timezone.utc)
|
|
|
|
|
)
|
|
|
|
|
backend.add_comment(issue_id, comment)
|
|
|
|
|
|
|
|
|
|
def read_agent_messages(issue_id, message_type=None):
|
|
|
|
|
"""Read structured 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 message_type is None or msg['type'] == message_type:
|
|
|
|
|
messages.append(msg)
|
|
|
|
|
except (IndexError, json.JSONDecodeError):
|
|
|
|
|
continue
|
|
|
|
|
return messages
|
|
|
|
|
|
|
|
|
|
# Usage:
|
|
|
|
|
post_agent_message(42, 'implementation_complete', {
|
|
|
|
|
'files_changed': ['src/auth.py', 'tests/test_auth.py'],
|
|
|
|
|
'tests_passing': True,
|
|
|
|
|
'coverage': 95.2
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Later, reviewer agent reads:
|
|
|
|
|
results = read_agent_messages(42, 'implementation_complete')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Synchronization and Backup
|
|
|
|
|
|
|
|
|
|
### Sync Local and Remote
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Pull all issues to local backup
|
|
|
|
|
issue backend add backup local
|
2026-09-14 04:52:58 +02:00
|
|
|
issue sync pull forgejo-remote backup
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
# Work offline with local backend
|
|
|
|
|
issue backend set-default backup
|
|
|
|
|
issue create "Offline work item" --label=offline
|
|
|
|
|
|
|
|
|
|
# Sync back when online
|
2026-09-14 04:52:58 +02:00
|
|
|
issue sync push backup forgejo-remote
|
2025-12-17 19:32:37 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Conflict Handling
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Check for conflicts before sync
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
from issue_core.cli.sync_commands import sync_pull
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
sync_pull(source='remote', target='local', dry_run=True)
|
|
|
|
|
except ConflictError as e:
|
|
|
|
|
# Conflicts detected
|
|
|
|
|
for conflict in e.conflicts:
|
|
|
|
|
print(f"Conflict on issue {conflict['issue_number']}")
|
|
|
|
|
print(f" Local updated: {conflict['local_updated']}")
|
|
|
|
|
print(f" Remote updated: {conflict['remote_updated']}")
|
|
|
|
|
|
|
|
|
|
# Resolve by choosing newer timestamp
|
|
|
|
|
sync_pull(source='remote', target='local', force=True)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Current Workarounds for Limitations
|
|
|
|
|
|
|
|
|
|
### Workaround 1: Agent Identity
|
|
|
|
|
|
|
|
|
|
Since "cli-user" is hardcoded, use labels or comments to indicate agent:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Add agent identifier to all operations
|
|
|
|
|
agent_id = "agent-coder-v1"
|
|
|
|
|
|
|
|
|
|
# In issue creation
|
|
|
|
|
labels.append(Label(name=f"created-by:{agent_id}"))
|
|
|
|
|
|
|
|
|
|
# In comments
|
|
|
|
|
comment.body = f"[{agent_id}] {actual_message}"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Workaround 2: Issue Claiming
|
|
|
|
|
|
|
|
|
|
No built-in locking, so use assignee + comment:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
def claim_issue(issue_number, agent_id, timeout_minutes=30):
|
|
|
|
|
issue = backend.get_issue_by_number(issue_number)
|
|
|
|
|
|
|
|
|
|
# Check if already claimed
|
|
|
|
|
if issue.assignees:
|
|
|
|
|
# Check claim age from comments
|
|
|
|
|
comments = backend.get_comments(issue.id)
|
|
|
|
|
claim_comments = [c for c in comments if 'CLAIMED' in c.body]
|
|
|
|
|
if claim_comments:
|
|
|
|
|
last_claim = claim_comments[-1].created_at
|
|
|
|
|
age = datetime.now(timezone.utc) - last_claim
|
|
|
|
|
if age.total_seconds() < timeout_minutes * 60:
|
|
|
|
|
return False # Still claimed
|
|
|
|
|
|
|
|
|
|
# Claim it
|
|
|
|
|
issue.assignees = [User(id=agent_id, username=agent_id)]
|
|
|
|
|
issue.state = IssueState.IN_PROGRESS
|
|
|
|
|
backend.update_issue(issue)
|
|
|
|
|
|
|
|
|
|
backend.add_comment(issue.id, Comment(
|
|
|
|
|
id=None,
|
|
|
|
|
body=f"CLAIMED by {agent_id} at {datetime.now(timezone.utc).isoformat()}",
|
|
|
|
|
author=User(id=agent_id, username=agent_id),
|
|
|
|
|
created_at=datetime.now(timezone.utc)
|
|
|
|
|
))
|
|
|
|
|
return True
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Workaround 3: Manual Repository Configuration
|
|
|
|
|
|
|
|
|
|
Create a setup script for each project:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
# setup-issue-tracking.sh
|
|
|
|
|
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
cat > .issue-core-config << EOF
|
2026-09-14 04:52:58 +02:00
|
|
|
FORGEJO_URL=https://forgejo.example.com
|
|
|
|
|
FORGEJO_OWNER=myorg
|
|
|
|
|
FORGEJO_REPO=myproject
|
|
|
|
|
FORGEJO_TOKEN_FILE=~/.secrets/forgejo-token
|
2025-12-17 19:32:37 +01:00
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
# Load config and configure backend
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
source .issue-core-config
|
2026-09-14 04:52:58 +02:00
|
|
|
export FORGEJO_TOKEN=$(cat $FORGEJO_TOKEN_FILE)
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
issue backend add $(basename $(pwd)) gitea <<INPUT
|
2026-09-14 04:52:58 +02:00
|
|
|
$FORGEJO_URL
|
|
|
|
|
$FORGEJO_OWNER
|
|
|
|
|
$FORGEJO_REPO
|
2025-12-17 19:32:37 +01:00
|
|
|
INPUT
|
|
|
|
|
|
|
|
|
|
issue backend set-default $(basename $(pwd))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Performance Considerations
|
|
|
|
|
|
|
|
|
|
### Efficient Querying
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# BAD: Get all issues then filter in Python
|
|
|
|
|
all_issues = backend.list_issues()
|
|
|
|
|
my_issues = [i for i in all_issues if i.assignees and i.assignees[0].username == 'agent-coder']
|
|
|
|
|
|
|
|
|
|
# GOOD: Use backend filtering
|
|
|
|
|
my_issues = backend.list_issues(IssueFilter(
|
|
|
|
|
assignee='agent-coder',
|
|
|
|
|
state='open'
|
|
|
|
|
))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Batch Operations
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# BAD: Update issues one by one
|
|
|
|
|
for issue_number in [1, 2, 3, 4, 5]:
|
|
|
|
|
issue = backend.get_issue_by_number(issue_number)
|
|
|
|
|
issue.labels.append(Label(name="batch-processed"))
|
|
|
|
|
backend.update_issue(issue)
|
|
|
|
|
|
|
|
|
|
# GOOD: Use local backend for bulk operations
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
from issue_core.backends.local import LocalSQLiteBackend
|
2025-12-17 19:32:37 +01:00
|
|
|
|
|
|
|
|
local = LocalSQLiteBackend()
|
|
|
|
|
local.connect({'db_path': '/tmp/batch.db'})
|
|
|
|
|
|
|
|
|
|
# Pull from remote
|
|
|
|
|
for issue_number in [1, 2, 3, 4, 5]:
|
|
|
|
|
issue = backend.get_issue_by_number(issue_number)
|
|
|
|
|
local.create_issue(issue)
|
|
|
|
|
|
|
|
|
|
# Bulk update locally
|
|
|
|
|
issues = local.list_issues()
|
|
|
|
|
for issue in issues:
|
|
|
|
|
issue.labels.append(Label(name="batch-processed"))
|
|
|
|
|
local.update_issue(issue)
|
|
|
|
|
|
|
|
|
|
# Push back to remote
|
|
|
|
|
for issue in local.list_issues():
|
|
|
|
|
backend.update_issue(issue)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Caching
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Cache issue list for short-lived operations
|
|
|
|
|
import functools
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
@functools.lru_cache(maxsize=1)
|
|
|
|
|
def get_open_issues_cached():
|
|
|
|
|
return backend.list_issues(IssueFilter(state='open'))
|
|
|
|
|
|
|
|
|
|
# Invalidate cache after 60 seconds
|
|
|
|
|
last_fetch = time.time()
|
|
|
|
|
if time.time() - last_fetch > 60:
|
|
|
|
|
get_open_issues_cached.cache_clear()
|
|
|
|
|
last_fetch = time.time()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Roadmap: Future Enhancements
|
|
|
|
|
|
|
|
|
|
### Phase 1: Auto-Configuration (v1.1)
|
|
|
|
|
- Automatic git remote detection
|
|
|
|
|
- Environment-variable-only setup
|
feat: rename to issue-core and add task ingestion endpoint
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>
2026-05-17 05:16:27 +02:00
|
|
|
- Per-repository `.issue-core/config.json` support
|
2025-12-17 19:32:37 +01:00
|
|
|
- `issue config detect` command
|
|
|
|
|
|
|
|
|
|
### Phase 2: Agent Features (v1.2)
|
|
|
|
|
- Agent identity management
|
|
|
|
|
- Issue claiming/locking API
|
|
|
|
|
- Structured metadata fields for agent state
|
|
|
|
|
- Webhook support for reactive agents
|
|
|
|
|
|
|
|
|
|
### Phase 3: Advanced Coordination (v2.0)
|
|
|
|
|
- Issue dependency tracking
|
|
|
|
|
- Query DSL: `is:open assignee:me label:bug,critical`
|
|
|
|
|
- Activity streams and event logs
|
|
|
|
|
- Multi-agent conflict resolution strategies
|
|
|
|
|
- Distributed locking for concurrent operations
|
|
|
|
|
|
|
|
|
|
## Examples Repository
|
|
|
|
|
|
|
|
|
|
See `examples/agents/` for complete working examples:
|
|
|
|
|
|
|
|
|
|
- `simple_task_executor.py` - Single agent claiming and executing tasks
|
|
|
|
|
- `multi_agent_pipeline.py` - Multiple agents in CI/CD-like workflow
|
|
|
|
|
- `human_in_loop.py` - Agents with human approval gates
|
|
|
|
|
- `monitoring_agent.py` - Agent that monitors issue health and sends alerts
|
|
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
### "Backend not configured"
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# List configured backends
|
|
|
|
|
issue backend list
|
|
|
|
|
|
|
|
|
|
# If empty, configure one
|
|
|
|
|
issue backend add myproject gitea
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### "Authentication failed"
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Check token is valid
|
|
|
|
|
issue backend test myproject
|
|
|
|
|
|
|
|
|
|
# Reconfigure with correct token
|
2026-09-14 04:52:58 +02:00
|
|
|
export FORGEJO_TOKEN="new-token"
|
2025-12-17 19:32:37 +01:00
|
|
|
issue backend remove myproject
|
|
|
|
|
issue backend add myproject gitea
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### "Issue not found"
|
|
|
|
|
|
|
|
|
|
```python
|
2026-09-14 04:52:58 +02:00
|
|
|
# Forgejo uses backend_id, not number
|
2025-12-17 19:32:37 +01:00
|
|
|
issue = backend.get_issue_by_number(42) # Correct
|
|
|
|
|
# issue = backend.get_issue("42") # Wrong - needs backend_id
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### "Sync conflicts"
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Force sync (overwrites target)
|
|
|
|
|
issue sync pull source target --force
|
|
|
|
|
|
|
|
|
|
# Or manually resolve
|
|
|
|
|
issue list --backend=source --format=json > source.json
|
|
|
|
|
issue list --backend=target --format=json > target.json
|
|
|
|
|
# Compare and decide which to keep
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Support
|
|
|
|
|
|
|
|
|
|
- **Documentation**: See `CLAUDE.md` for development guide
|
|
|
|
|
- **Tests**: Run `make test` to verify installation
|
|
|
|
|
- **Issues**: Report issues in the main markitect repository
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
MIT License - See LICENSE file
|