Files
gitea-infrastructure/.docs/DESIGN-gitea-agent-skill.md
Alex Mikhalev c6d425be63 feat(gitea-skill): Rewrite Gitea agent skill with verified API patterns
Rewrites SKILL.md with patterns verified against live Gitea 1.22.6:
- Milestones as project containers (project board API does not exist)
- Scoped workflow labels with mutual exclusivity
- Issue dependencies using IssueMeta format
- 1Password token retrieval via op read
- Default repo terraphim/agent-tasks with env var overrides

Adds setup-labels.sh for idempotent repo and label creation.
Includes research and design documents from disciplined development.
Adds pre-commit config adapted for infrastructure project.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:41:19 +01:00

612 lines
22 KiB
Markdown

# Implementation Plan: Gitea Agent Skill for Project/Task Management
**Phase**: 2 - Disciplined Design
**Date**: 2026-02-18
**Status**: DRAFT - Awaiting Approval
**Research Document**: .docs/RESEARCH-gitea-agent-skill.md
---
## 1. Overview
### Summary
Enhance the existing Gitea AI agent skill to support project and task management
workflows for AI agents. The core insight from Phase 1 research is that Gitea 1.22.6
has NO project board REST API, so milestones serve as the API-backed project container.
Labels implement a workflow state machine inspired by the br (beads_rust) pattern:
ready -> in_progress -> done.
### Approach
Option C from the research document: Dual-mode architecture combining gitea-mcp
(official MCP server with 75+ tools) for standard operations with curl/jq shell
helpers in the skill document for milestone management, issue dependencies, time
tracking, and workflow orchestration -- areas where gitea-mcp has gaps.
### Scope (5/25 Rule Applied)
**Top 5 (Will Do)**:
1. Rewrite SKILL.md with correct API endpoints (remove fake project board endpoints)
2. Add milestone-based project management (create, list, progress tracking)
3. Add label-based workflow state machine (ready/in_progress/blocked/done)
4. Add shell helper functions with JSON output for agent consumption
5. Add integration test script validating all operations against live Gitea
**Next 20 (Will NOT Do)**:
- Custom Gitea plugin for project boards
- Gitea Actions workflow templates
- Webhook-based event processing
- Dashboard or web UI
- Multi-instance Gitea federation
- Custom MCP server wrapping Gitea API
- Kanban board simulation via API
- Automated code review integration
- Release management automation
- CI/CD pipeline integration
---
## 2. Architecture
### Component Diagram
```
+--------------------------------------------------+
| AI Agent |
| (Claude Code / MCP Client) |
+--------+------------------+----------------------+
| |
v v
+----------------+ +------------------+
| gitea-mcp | | SKILL.md |
| (MCP Server) | | (Shell Helpers) |
| - repos | | - milestones |
| - issues CRUD | | - dependencies |
| - PRs | | - workflow labels |
| - users/orgs | | - progress track |
| - branches | | - time tracking |
| - files | | - setup-labels |
+-------+--------+ +--------+---------+
| |
v v
+--------------------------------------------------+
| Gitea REST API v1 |
| https://git.terraphim.cloud/api/v1 |
+--------------------------------------------------+
| Gitea 1.22.6 Instance |
+--------------------------------------------------+
```
### Data Flow
1. Agent reads SKILL.md for available operations and patterns
2. For standard CRUD (repos, issues, PRs): agent uses gitea-mcp tools
3. For milestones, dependencies, workflow: agent uses curl/jq helpers from SKILL.md
4. All helpers use GITEA_TOKEN from 1Password via op read
5. All helpers output JSON for machine-readable agent consumption
6. Labels encode workflow state; milestones encode project containers
### Key Design Decisions
| Decision | Rationale |
|----------|-----------|
| Milestones as projects | Only API-backed grouping in Gitea 1.22.6; project board API does not exist |
| Labels for workflow state | Lightweight, API-accessible, visible in UI, no custom code needed |
| curl/jq helpers (not scripts) | Copy-paste into agent context; no installation; works everywhere |
| JSON output from all helpers | Machine-readable for agent pipelines; parseable with jq |
| 1Password op read for tokens | Consistent with existing infrastructure; no plaintext secrets |
| Dual MCP + skill approach | gitea-mcp covers 80% of operations; skill fills the 20% gap |
### Eliminated Options
| Option | Why Eliminated |
|--------|---------------|
| Option A: Skill-only | Ignores existing gitea-mcp with 75+ tools; duplicates work |
| Option B: MCP-only | gitea-mcp lacks milestones, dependencies, workflow labels |
| Custom Gitea plugin | Over-engineered; requires Go development and Gitea rebuilds |
| GitHub Projects API shim | Wrong abstraction; Gitea is not GitHub |
| Database-backed state | External dependency; Gitea already stores state in issues/labels |
### Simplicity Check
- [x] Could be explained in one paragraph? YES: "Use milestones as projects, labels as workflow states, curl/jq for API calls, gitea-mcp for standard ops"
- [x] Junior developer could implement from this plan? YES: all operations are curl commands with documented endpoints
- [x] No speculative features? YES: every feature maps to a concrete user request from the design brief
- [x] Every component has a clear owner? YES: SKILL.md owned by this repo; gitea-mcp is external dependency
---
## 3. File Changes
### File 1: `.agents/skills/gitea/SKILL.md` (MODIFY)
**What changes**: Complete rewrite. Remove incorrect project board endpoints (lines 222-263).
Restructure into: Configuration, Authentication, Core Operations (repos, issues, PRs, milestones,
labels, releases), Agent Workflow (state machine, helpers), MCP Integration, Error Handling.
**Why**: Current file has non-existent API endpoints. Need milestone-based project management
and workflow state machine for agent task tracking.
**Size estimate**: ~500 lines (currently ~366 lines)
### File 2: `.agents/skills/gitea/setup-labels.sh` (NEW)
**What changes**: Shell script that creates the 12 workflow labels on a Gitea repository.
Idempotent (checks for existing labels before creating). Uses GITEA_TOKEN and GITEA_URL
environment variables.
**Why**: Labels are the foundation of the workflow state machine. Need automated setup
rather than manual UI clicks.
**Size estimate**: ~80 lines
### File 3: `.agents/skills/gitea/test-gitea-skill.sh` (NEW)
**What changes**: Integration test script that validates all skill operations against a
live Gitea instance. Creates a test repo, runs all operations, validates JSON output,
cleans up. Uses real API calls (no mocks).
**Why**: User instructions mandate no mocks. Need to verify all documented endpoints
actually work with Gitea 1.22.6.
**Size estimate**: ~200 lines
---
## 4. API Design
### Environment Variables
```bash
GITEA_URL="https://git.terraphim.cloud" # Gitea instance URL
GITEA_TOKEN="$(op read op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential)" # API token from 1Password
GITEA_OWNER="${GITEA_OWNER:-terraphim}" # Org or user owning the repo
GITEA_REPO="${GITEA_REPO:-agent-tasks}" # Default repo for task tracking (user-overridable)
```
### Shell Helper Functions
All functions output JSON. All functions use these conventions:
- Exit 0 on success, non-zero on failure
- Print JSON to stdout
- Print errors to stderr
- Accept positional arguments (no flags)
#### 4.1 `gitea_api` - Base API caller
```bash
# Usage: gitea_api METHOD ENDPOINT [DATA]
# Returns: JSON response from Gitea API
gitea_api() {
local method="$1" endpoint="$2" data="${3:-}"
local args=(-s -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json")
if [ -n "$data" ]; then
args+=(-X "$method" -d "$data")
else
args+=(-X "$method")
fi
curl "${args[@]}" "$GITEA_URL/api/v1$endpoint"
}
```
#### 4.2 `gitea_create_project` - Create milestone as project
```bash
# Usage: gitea_create_project TITLE [DESCRIPTION] [DUE_DATE] [OWNER] [REPO]
# Returns: JSON milestone object with id, title, state, open_issues, closed_issues
# DUE_DATE format: 2026-03-01T00:00:00Z
# OWNER/REPO default to GITEA_OWNER/GITEA_REPO (terraphim/agent-tasks)
gitea_create_project() {
local title="$1"
local desc="${2:-}" due="${3:-}"
local owner="${4:-$GITEA_OWNER}" repo="${5:-$GITEA_REPO}"
local data
data=$(jq -n --arg t "$title" --arg d "$desc" --arg due "$due" \
'{title: $t, description: $d} + (if $due != "" then {due_on: $due} else {} end)')
gitea_api POST "/repos/$owner/$repo/milestones" "$data"
}
```
#### 4.3 `gitea_create_task` - Create issue with milestone and labels
```bash
# Usage: gitea_create_task TITLE BODY MILESTONE_ID [LABEL_IDS] [OWNER] [REPO]
# LABEL_IDS: comma-separated label IDs (e.g., "1,2,3")
# Returns: JSON issue object
gitea_create_task() {
local title="$1" body="$2" milestone="$3"
local labels="${4:-}"
local owner="${5:-$GITEA_OWNER}" repo="${6:-$GITEA_REPO}"
local data
data=$(jq -n --arg t "$title" --arg b "$body" --argjson m "$milestone" \
'{title: $t, body: $b, milestone: $m}')
if [ -n "$labels" ]; then
data=$(echo "$data" | jq --argjson l "[$labels]" '. + {labels: $l}')
fi
gitea_api POST "/repos/$owner/$repo/issues" "$data"
}
```
#### 4.4 `gitea_set_labels` - Replace all labels on an issue
```bash
# Usage: gitea_set_labels ISSUE_INDEX LABEL_IDS [OWNER] [REPO]
# LABEL_IDS: comma-separated label IDs
# Returns: JSON array of label objects
gitea_set_labels() {
local index="$1" labels="$2"
local owner="${3:-$GITEA_OWNER}" repo="${4:-$GITEA_REPO}"
gitea_api PUT "/repos/$owner/$repo/issues/$index/labels" "{\"labels\": [$labels]}"
}
```
#### 4.5 `gitea_ready` - Mark task as ready for work
```bash
# Usage: gitea_ready ISSUE_INDEX [OWNER] [REPO]
# Adds "workflow:ready" label, removes other workflow labels
gitea_ready() {
local index="$1"
local owner="${2:-$GITEA_OWNER}" repo="${3:-$GITEA_REPO}"
local ready_id
ready_id=$(gitea_api GET "/repos/$owner/$repo/labels" | jq -r '.[] | select(.name=="workflow:ready") | .id')
gitea_set_labels "$owner" "$repo" "$index" "$ready_id"
}
```
#### 4.6 `gitea_claim` - Claim task (mark in-progress, assign self)
```bash
# Usage: gitea_claim ISSUE_INDEX [USERNAME] [OWNER] [REPO]
# Sets "workflow:in-progress" label, assigns user
gitea_claim() {
local index="$1" user="${2:-}"
local owner="${3:-$GITEA_OWNER}" repo="${4:-$GITEA_REPO}"
local ip_id
ip_id=$(gitea_api GET "/repos/$owner/$repo/labels" | jq -r '.[] | select(.name=="workflow:in-progress") | .id')
gitea_set_labels "$owner" "$repo" "$index" "$ip_id"
if [ -n "$user" ]; then
gitea_api POST "/repos/$owner/$repo/issues/$index/assignees" "{\"assignees\": [\"$user\"]}"
fi
}
```
#### 4.7 `gitea_close_task` - Complete task
```bash
# Usage: gitea_close_task ISSUE_INDEX [COMMENT] [OWNER] [REPO]
# Sets "workflow:done" label, closes issue, optionally adds comment
gitea_close_task() {
local index="$1" comment="${2:-}"
local owner="${3:-$GITEA_OWNER}" repo="${4:-$GITEA_REPO}"
local done_id
done_id=$(gitea_api GET "/repos/$owner/$repo/labels" | jq -r '.[] | select(.name=="workflow:done") | .id')
gitea_set_labels "$owner" "$repo" "$index" "$done_id"
gitea_api PATCH "/repos/$owner/$repo/issues/$index" '{"state": "closed"}'
if [ -n "$comment" ]; then
local cdata
cdata=$(jq -n --arg b "$comment" '{body: $b}')
gitea_api POST "/repos/$owner/$repo/issues/$index/comments" "$cdata"
fi
}
```
#### 4.8 `gitea_progress` - Show project progress
```bash
# Usage: gitea_progress [MILESTONE_ID] [OWNER] [REPO]
# Returns: JSON with open/closed counts, percentage, task list
gitea_progress() {
local milestone="${1:-}"
local owner="${2:-$GITEA_OWNER}" repo="${3:-$GITEA_REPO}"
if [ -n "$milestone" ]; then
local ms
ms=$(gitea_api GET "/repos/$owner/$repo/milestones/$milestone")
echo "$ms" | jq '{
milestone: .title,
open: .open_issues,
closed: .closed_issues,
total: (.open_issues + .closed_issues),
percent_complete: (if (.open_issues + .closed_issues) > 0
then (.closed_issues * 100 / (.open_issues + .closed_issues))
else 0 end)
}'
else
gitea_api GET "/repos/$owner/$repo/milestones?state=open"
fi
}
```
---
## 5. Label Scheme
### Workflow Labels (mutually exclusive)
| Label | Color | Description |
|-------|-------|-------------|
| `workflow:ready` | `#0e8a16` (green) | Task is ready for an agent to pick up |
| `workflow:in-progress` | `#fbca04` (yellow) | Task is actively being worked on |
| `workflow:blocked` | `#d93f0b` (red) | Task is blocked by a dependency |
| `workflow:done` | `#6f42c1` (purple) | Task is complete |
### Priority Labels
| Label | Color | Description |
|-------|-------|-------------|
| `priority:critical` | `#b60205` (dark red) | Must be done immediately |
| `priority:high` | `#d93f0b` (red) | Should be done this cycle |
| `priority:medium` | `#fbca04` (yellow) | Normal priority |
| `priority:low` | `#0e8a16` (green) | Nice to have |
### Type Labels
| Label | Color | Description |
|-------|-------|-------------|
| `type:feature` | `#1d76db` (blue) | New functionality |
| `type:bug` | `#d93f0b` (red) | Defect fix |
| `type:task` | `#5319e7` (violet) | General task |
| `type:research` | `#006b75` (teal) | Investigation/spike |
---
## 6. Agent Workflow State Machine
```
create_task
|
v
+---------------+
| workflow:ready |<--------+
+-------+-------+ |
| |
gitea_claim (unblock)
| |
v |
+--------------------+ +------------------+
| workflow:in-progress|->| workflow:blocked |
+---------+----------+ +------------------+
|
gitea_close_task
|
v
+---------------+
| workflow:done |
| (issue closed) |
+---------------+
```
### Workflow Commands (br-inspired)
| br Command | Gitea Equivalent | Description |
|------------|------------------|-------------|
| `br ready` | `gitea_ready 1` | Mark task #1 ready |
| `br update ID --status in_progress` | `gitea_claim 1 myuser` | Claim and start task #1 |
| `br close ID` | `gitea_close_task 1 "Done"` | Complete task #1 |
| `br list --status ready` | `gitea_api GET /repos/$GITEA_OWNER/$GITEA_REPO/issues?labels=workflow:ready` | List ready tasks |
| `br status` | `gitea_progress 1` | Show milestone #1 progress |
All helpers default to `GITEA_OWNER=terraphim` and `GITEA_REPO=agent-tasks`. Override with env vars or trailing arguments.
---
## 7. Test Strategy
### Approach
Integration tests against the live Gitea instance at https://git.terraphim.cloud.
No mocks. Each test creates its own resources and cleans up afterward. Tests run
sequentially to avoid race conditions.
### Test Cases
| # | Test | Validates | Expected Result |
|---|------|-----------|-----------------|
| 1 | Create test repository | `POST /repos` | 201, JSON with repo name |
| 2 | Create labels via setup-labels.sh | `POST /repos/{owner}/{repo}/labels` | 12 labels created |
| 3 | Create milestone (project) | `gitea_create_project` | 201, JSON with milestone id |
| 4 | Create issue (task) with milestone | `gitea_create_task` | 201, JSON with issue number |
| 5 | Mark task ready | `gitea_ready` | Label set to workflow:ready |
| 6 | Claim task | `gitea_claim` | Label set to workflow:in-progress, assignee set |
| 7 | Check project progress | `gitea_progress` | JSON with 0% complete (1 open, 0 closed) |
| 8 | Close task | `gitea_close_task` | Issue state=closed, label=workflow:done |
| 9 | Check progress after close | `gitea_progress` | JSON with 100% complete |
| 10 | Delete test repository | `DELETE /repos/{owner}/{repo}` | 204, cleanup successful |
### Test Runner
```bash
# Run all tests
./test-gitea-skill.sh
# Requirements:
# - GITEA_TOKEN set (or op read available)
# - GITEA_URL set (defaults to https://git.terraphim.cloud)
# - curl, jq installed
```
### Validation Criteria
- All 10 tests pass against live Gitea 1.22.6
- All helper functions return valid JSON (validated with jq)
- Cleanup succeeds (no orphaned test repos)
- No hardcoded secrets in any file
---
## 8. Implementation Steps
### Step 1: Create setup-labels.sh (30 min)
**Files**: `.agents/skills/gitea/setup-labels.sh`
**Actions**:
1. Create `terraphim/agent-tasks` repo if it doesn't exist (idempotent)
2. Write script that creates all 12 labels
3. Make idempotent (check existing labels first)
4. Use GITEA_TOKEN, GITEA_URL, GITEA_OWNER, GITEA_REPO environment variables
5. Output JSON summary of created/existing labels
**Commit message**: "Add label setup script for Gitea agent workflow"
### Step 2: Rewrite SKILL.md (2 hours)
**Files**: `.agents/skills/gitea/SKILL.md`
**Actions**:
1. Keep frontmatter and system prompt
2. Update Configuration section with 1Password integration
3. Keep correct existing sections (users, orgs, repos, issues, PRs, releases)
4. Remove incorrect project board endpoints (lines 222-263)
5. Add Milestones section (create, list, update, delete)
6. Add Agent Workflow section (state machine, all 8 helper functions)
7. Add MCP Integration section (gitea-mcp setup and usage)
8. Add Dependencies section (issue blocking API)
9. Update Error Handling section
**Commit message**: "Rewrite Gitea skill with milestone-based project management"
### Step 3: Create test script (1 hour)
**Files**: `.agents/skills/gitea/test-gitea-skill.sh`
**Actions**:
1. Write integration test runner
2. Implement all 10 test cases
3. Add setup/teardown (create/delete test repo)
4. Add JSON validation for all outputs
5. Add color-coded pass/fail output
**Commit message**: "Add integration test script for Gitea skill"
### Step 4: Verify 1Password integration (30 min)
**Actions** (no file changes):
1. Verify gitea-token item exists in TerraphimPlatform vault (or create it)
2. Test `op read op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential`
3. Verify token has required scopes (repo, admin:org, write:user)
**Commit message**: None (verification only; document findings in test output)
### Step 5: Run tests and iterate (1 hour)
**Actions**:
1. Run setup-labels.sh against a test repository
2. Run test-gitea-skill.sh
3. Fix any failing tests
4. Verify cleanup succeeded
**Commit message**: "Fix issues found during integration testing" (if needed)
**Total estimated time**: ~5 hours
---
## 9. Risk Mitigation
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Gitea API changes in future version | Low | Medium | Pin to 1.22.6 in docs; version check in test script |
| gitea-mcp adds milestone support | Low | Low | Reduces need for curl helpers; update skill to prefer MCP |
| 1Password token not configured | Medium | High | Test script checks for token before running; clear error message |
| Label name collision with existing labels | Low | Low | setup-labels.sh checks for existing labels; skips if present |
| Test repo cleanup fails | Low | Medium | Test script has explicit cleanup step; documents manual cleanup |
| Rate limiting on bulk label creation | Low | Low | Sequential creation with small delay if needed |
### Rollback Plan
All changes are in `.agents/skills/gitea/` directory. Rollback = `git revert` the commits.
No infrastructure changes. No database migrations. No service restarts needed.
---
## 10. Dependencies
| Dependency | Type | Status |
|-----------|------|--------|
| Gitea 1.22.6 running at git.terraphim.cloud | Runtime | Available |
| GITEA_TOKEN in 1Password TerraphimPlatform vault | Secret | Needs verification |
| curl | Tool | Available on bigbox |
| jq | Tool | Available on bigbox |
| gitea-mcp (optional) | MCP Server | Not yet installed; documented for agent use |
---
## 11. Open Items (Updated After Live Verification)
1. **GITEA_TOKEN in 1Password**: RESOLVED. Token exists at
`op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential`.
Authenticated user is `root` (admin). Update SKILL.md to use this path.
2. **gitea-mcp installation**: The skill documents gitea-mcp setup but does not install it.
Installation is a separate task (requires Go 1.24+ or pre-compiled binary).
3. **Issue dependencies API**: RESOLVED. Works with `IssueMeta` body format:
`{"owner": "...", "repo": "...", "index": N}`. Supports cross-repo dependencies.
Update design helper functions to use correct format.
4. **Scoped labels**: RESOLVED. Mutual exclusivity confirmed. Using `exclusive: true`
on label creation + scope prefix (e.g., `status/`) enforces one-label-per-scope.
5. **Default tracking repo**: RESOLVED. Default is `terraphim/agent-tasks` via
`GITEA_OWNER=terraphim` and `GITEA_REPO=agent-tasks`. User can override both
env vars to point at any repo. The `agent-tasks` repo will be created during
setup if it doesn't exist.
---
## 12. Approval Checklist
Before proceeding to Phase 3 (Implementation), confirm:
- [ ] Architecture (milestones as projects, labels as workflow) is acceptable
- [ ] File change list is complete and correct
- [ ] Shell helper function signatures are acceptable
- [ ] Label scheme (12 labels, 3 categories) is acceptable
- [ ] Test strategy (10 integration tests, no mocks) is acceptable
- [ ] Implementation sequence (5 steps, ~5 hours) is reasonable
---
## Appendix A: Gitea 1.22.6 Milestone API Reference
```
POST /repos/{owner}/{repo}/milestones - Create milestone
GET /repos/{owner}/{repo}/milestones - List milestones
GET /repos/{owner}/{repo}/milestones/{id} - Get milestone
PATCH /repos/{owner}/{repo}/milestones/{id} - Update milestone
DELETE /repos/{owner}/{repo}/milestones/{id} - Delete milestone
```
Response fields: `id`, `title`, `description`, `state` (open/closed),
`open_issues`, `closed_issues`, `due_on`, `created_at`, `updated_at`.
## Appendix B: Gitea 1.22.6 Label API Reference
```
POST /repos/{owner}/{repo}/labels - Create label
GET /repos/{owner}/{repo}/labels - List labels
PATCH /repos/{owner}/{repo}/labels/{id} - Update label
DELETE /repos/{owner}/{repo}/labels/{id} - Delete label
POST /repos/{owner}/{repo}/issues/{index}/labels - Add labels to issue
PUT /repos/{owner}/{repo}/issues/{index}/labels - Replace issue labels
DELETE /repos/{owner}/{repo}/issues/{index}/labels - Clear issue labels
DELETE /repos/{owner}/{repo}/issues/{index}/labels/{id} - Remove label from issue
```
## Appendix C: br Workflow Mapping Reference
| br Concept | Gitea Mapping |
|------------|--------------|
| Bead | Issue |
| Bead status | Label (workflow:*) |
| Bead priority | Label (priority:*) |
| Bead type | Label (type:*) |
| Project/Sprint | Milestone |
| Assignment | Issue assignee |
| Dependencies | Issue dependencies API |
| Progress | Milestone open/closed ratio |
| Comments | Issue comments |
| Close | Issue state = closed + workflow:done label |