diff --git a/.claude/rules/repo-identity.md b/.claude/rules/repo-identity.md index f2c71e2..fec783c 100644 --- a/.claude/rules/repo-identity.md +++ b/.claude/rules/repo-identity.md @@ -1,4 +1,4 @@ -**Purpose:** Authoritative task lifecycle manager for the Coulomb org. Backend-agnostic CLI + REST ingestion endpoint for tasks from activity-core's IssueSink. Pluggable backends (Gitea, SQLite, GitHub). Renamed from issue-facade on 2026-05-17. +**Purpose:** Authoritative task lifecycle manager for the Coulomb org. Backend-agnostic CLI + REST ingestion endpoint for tasks from activity-core's IssueSink. Pluggable backends (Forgejo, SQLite, GitHub). Renamed from issue-facade on 2026-05-17. **Domain:** infotech **Repo slug:** issue-core diff --git a/AGENTS.md b/AGENTS.md index 6b1ef81..038ec25 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Repo Identity -**Purpose:** Authoritative task lifecycle manager for the Coulomb org. Backend-agnostic CLI + REST ingestion endpoint for tasks from activity-core's IssueSink. Pluggable backends (Gitea, SQLite, GitHub). Renamed from issue-facade on 2026-05-17. +**Purpose:** Authoritative task lifecycle manager for the Coulomb org. Backend-agnostic CLI + REST ingestion endpoint for tasks from activity-core's IssueSink. Pluggable backends (Forgejo, SQLite, GitHub). Renamed from issue-facade on 2026-05-17. **Domain:** infotech **Repo slug:** issue-core diff --git a/AGENT_INTEGRATION.md b/AGENT_INTEGRATION.md index 297ca95..6a0daeb 100644 --- a/AGENT_INTEGRATION.md +++ b/AGENT_INTEGRATION.md @@ -1,24 +1,55 @@ # Agent Integration Guide -**Issue Core for Autonomous Coding Agent Coordination** +**Issue Core for external tracker projection** — not the fleet ops queue. ## Purpose -The **Issue Core** capability provides a standardized interface for autonomous coding agents to operate on **external issue trackers** (GitHub, GitLab, Gitea/Forgejo). Instead of agents learning platform-specific APIs, they use a unified abstraction. +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`). -**Fleet note (2026-07-20):** 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`. +**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. ### When to use a tracker (and when not to) -**Fleet claim/execute** runs on work records (repo files + state-hub), not on -Forgejo by default. Use issue-core when: +**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: -- A counterparty or OSS workflow lives in Gitea/GitHub/Jira +- A counterparty or OSS workflow lives in Forgejo/GitHub/Jira - 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 -Do **not** treat issue-core as the org task board or as origin of intake/tasks. -Findings → `kind: intake` + promotion; optional later projection. +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. ## Current Status: Production-Ready with Manual Setup @@ -31,7 +62,7 @@ Findings → `kind: intake` + promotion; optional later projection. - Milestone operations - Comment threads -✅ **Gitea Backend** (Production-Ready) +✅ **Forgejo Backend** (Production-Ready; Gitea-compatible API) - Complete API integration - Rate limiting and error handling - State mapping (open/in_progress/blocked → open/closed) @@ -76,18 +107,19 @@ pip install -e . ### 2. Backend Configuration (One-Time Setup) -**For Gitea Projects:** +**For Forgejo Projects:** ```bash -# Configure Gitea backend -export GITEA_API_TOKEN="your-token-here" +# 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 issue backend add my-project gitea # Prompts for: -# - Gitea URL: https://gitea.example.com +# - Forgejo URL: https://forgejo.example.com # - Owner: your-org # - Repo: your-project -# - Token: (reads from GITEA_API_TOKEN) +# - Token: (reads FORGEJO_TOKEN, then deprecated GITEA_* aliases) # Verify connection issue backend test my-project @@ -187,8 +219,8 @@ from datetime import datetime, timezone backend = GiteaBackend() backend.connect({ - 'base_url': 'https://gitea.example.com', - 'token': os.environ['GITEA_API_TOKEN'], + 'base_url': os.environ.get('FORGEJO_URL', 'https://forgejo.example.com'), + 'token': os.environ.get('FORGEJO_TOKEN') or os.environ['GITEA_API_TOKEN'], 'owner': 'myorg', 'repo': 'myproject' }) @@ -236,10 +268,10 @@ import os # Initialize backend 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'] + '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'] }) # Query issues @@ -421,14 +453,14 @@ results = read_agent_messages(42, 'implementation_complete') ```bash # Pull all issues to local backup issue backend add backup local -issue sync pull gitea-remote backup +issue sync pull forgejo-remote backup # Work offline with local backend issue backend set-default backup issue create "Offline work item" --label=offline # Sync back when online -issue sync push backup gitea-remote +issue sync push backup forgejo-remote ``` ### Conflict Handling @@ -509,20 +541,20 @@ Create a setup script for each project: # setup-issue-tracking.sh cat > .issue-core-config << EOF -GITEA_URL=https://gitea.example.com -GITEA_OWNER=myorg -GITEA_REPO=myproject -GITEA_TOKEN_FILE=~/.secrets/gitea-token +FORGEJO_URL=https://forgejo.example.com +FORGEJO_OWNER=myorg +FORGEJO_REPO=myproject +FORGEJO_TOKEN_FILE=~/.secrets/forgejo-token EOF # Load config and configure backend source .issue-core-config -export GITEA_API_TOKEN=$(cat $GITEA_TOKEN_FILE) +export FORGEJO_TOKEN=$(cat $FORGEJO_TOKEN_FILE) issue backend add $(basename $(pwd)) gitea < - Backend-agnostic connector to external issue trackers (Gitea/Forgejo, SQLite). + Backend-agnostic connector to external issue trackers (Forgejo, SQLite). Maps work-record UUIDs to external issue ids. Not the origin of fleet work records. # What problems this capability solves purpose: primary: External tracker projection and ops (work-record-aware connector) problems_solved: - - Direct API calls to Gitea/GitHub/GitLab (credential sprawl) + - Direct API calls to Forgejo/GitHub/GitLab (credential sprawl) - No durable link from work-record UUID to tracker issue - Platform-specific agent code for tracker CRUD - Offline SQLite cache / backend sync for tracker data @@ -21,7 +21,7 @@ purpose: # When agents should use this capability usage_rules: MUST_USE_INSTEAD_OF: - - "Direct Gitea API calls (requests.post to /api/v1/repos/...)" + - "Direct Forgejo API calls (requests.post to /api/v1/repos/...)" - "GitHub CLI (gh issue create/list/...)" - "GitLab CLI (glab issue create/list/...)" - "Python libraries (PyGithub, python-gitlab) for routine tracker ops" @@ -67,7 +67,7 @@ integration: required: true method: manual # v1.0 - auto in v1.1 steps: - - "Export GITEA_API_TOKEN environment variable" + - "Export FORGEJO_TOKEN environment variable (GITEA_API_TOKEN is a deprecated alias)" - "Run: issue backend add myproject gitea" - "Provide: URL, owner, repo when prompted" - "Run: issue backend set-default myproject" @@ -115,8 +115,9 @@ efficiency: credentials: method: environment_variables variables: - - GITEA_API_TOKEN - - GITEA_URL (optional with config) + - FORGEJO_TOKEN (preferred; FORGEJO_API_TOKEN also accepted) + - GITEA_API_TOKEN / GITEA_TOKEN (deprecated aliases) + - FORGEJO_URL (optional with config; GITEA_URL deprecated alias) security: - "Tokens never in code or logs" @@ -259,7 +260,7 @@ support: solution: "Run: issue backend add " - problem: "Authentication failed" - solution: "Check GITEA_API_TOKEN is set and valid" + solution: "Check FORGEJO_TOKEN is set and valid (legacy GITEA_API_TOKEN still accepted)" - problem: "Command not found: issue" solution: "Run: pip install -e capabilities/issue-core/" diff --git a/INTENT.md b/INTENT.md index 14bc3eb..89229b3 100644 --- a/INTENT.md +++ b/INTENT.md @@ -9,11 +9,12 @@ state-hub, under a closed kind registry (`workplan`, `task`, `intake`, `the-custodian/canon/standards/work-record-types_v0.1.md` and the founder- reviewed architecture draft (`WorkOrchestrationArchitectureDraft.md` v0.2). -External issue trackers (Forgejo / Gitea, GitHub, Jira, …) remain necessary +External issue trackers (Forgejo, GitHub, Jira, …) remain necessary when a human counterparty, open-source workflow, or third-party process lives there. They are **not** the fleet coordination substrate and are **not** a work-record kind. Canon is explicit: *issue-core issues become external -projections only.* +projections only.* The self-hosted forge product is **Forgejo** only +(ACT-ADR-005); Gitea is not a supported second product or migration target. issue-core exists so that, **when an external tracker is actually in use**, the fleet has one backend-agnostic surface to project, query, and update @@ -32,9 +33,11 @@ calls with no stable back-reference to the work record. issue-core was originally built and documented as a **task landing zone**: a single place where humans, activity-core, and agents filed work via CLI / -REST, with Gitea as the default store. That framing contradicted file-first -work records and produced a live incident (`daily-todo-md-stale-review` → -IssueSink → Forgejo issues that were never fleet work). +REST, with the self-hosted forge as the default store (product language then +said Gitea; the fleet forge is **Forgejo**). That framing contradicted +file-first work records and produced a live incident +(`daily-todo-md-stale-review` → IssueSink → Forgejo issues that were never +fleet work). Architecture draft §4.2 and the work-record types standard (CUST-WP-0060) retargeted purpose: **connector, not origin**. Capability (CRUD, backends, @@ -51,13 +54,16 @@ execution loop. - **Tracker CRUD** on configured backends: create, read, update, close / reopen, comment (and related label / assignee / milestone operations). -- **Backends:** local SQLite (offline store / cache), Gitea (Forgejo- - compatible in deployment). Further backends (GitHub, GitLab, Jira) are - product growth, not yet implemented. +- **Backends:** local SQLite (offline store / cache), **Forgejo** (Gitea- + compatible API; Python module `issue_core.backends.gitea`, backend type + string `gitea`). Further backends (GitHub, GitLab, Jira) are product + growth, not yet implemented. Gitea is not a second supported product. - **CLI** (`issue` / `issue-core`) and **Python library** for direct backend use. -- **REST** (`issue serve`, optional `[api]` extra): intentional create - (`POST /issues/`), list/get/claim (`GET`/`PATCH /issues/`). -- **Backend↔backend sync** via CLI (e.g. Gitea ↔ SQLite) — *not* the same as +- **REST** (`issue serve`, optional `[api]` extra): intentional **external + tracker** create (`POST /issues/`), list/get (`GET`/`PATCH /issues/`). + REST claim/list is for tracker issues already on a backend — **not** the + fleet ops claim queue (see below). +- **Backend↔backend sync** via CLI (e.g. Forgejo ↔ SQLite) — *not* the same as work-record boundary sync below. - **Optional intentional ingestion** of TaskSpec payloads for clients that deliberately create tracker issues (not the fleet path for internal findings). @@ -104,10 +110,23 @@ connector must add **no coordination load** to the internal loop. Emitter policy: activity-core **ACTIVITY-WP-0022** (from ISSUE-WP-0004-T05 / CUST-WP-0060). +- **Not the fleet ops claim queue.** Internal scheduled automation + (FI daily brief, Binky rhythm, mail intake, …) **claim and execute** via + activity-core **`ops_run`** ([ACT-ADR-005](../activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md)). + issue-core does **not** provide that queue. `POST /issues/` creates or + links an **external tracker** issue only. The default activity-core sink + remains state-hub progress + `ops_run` — **not** REST to issue-core for + Binky/FI. Do not poll issue-core as the primary automation loop. + + ACT-ADR-005: *issue-core’s correct role is a connector facade over + external trackers (Forgejo, GitHub, Jira, …). It is not the origin of + work records and not the default internal ops queue. Gitea is out of + scope for this fleet; the self-hosted forge is Forgejo.* + - **Not a second autonomy or budget model.** `lane`, `tags`, and budgets live on the work record. Projections carry only what external collab needs (title, body, agreed labels); they must not invent parallel lane/budget - semantics on Gitea labels without explicit rules. + semantics on Forgejo labels without explicit rules. - **Not a project manager, spawn audit trail, event bus, notification system, or workflow engine.** Plans and dependencies are workplan tooling; @@ -151,8 +170,8 @@ cancel`) and tracker **`IssueState`** (`open` / `in_progress` / `blocked` / +---+------+------+--+ | | | v v v - Gitea/ SQLite GitHub - Forgejo cache (planned) + Forgejo SQLite GitHub + cache (planned) ``` **Primary fleet path (coordination):** @@ -205,3 +224,7 @@ cancel`) and tracker **`IssueState`** (`open` / `in_progress` / `blocked` / connector decision. - `the-custodian/canon/architecture/adr-001-workplans-as-repo-artefacts.md`. - activity-core **ACTIVITY-WP-0022** — IssueSink default policy (emitter side). +- activity-core **ACT-ADR-005** — ops runs vs work records; issue-core is not + the ops claim queue. +- `workplans/ISSUE-WP-0006-forgejo-only-projection-boundary.md` — Forgejo-only + product language and projection boundary. diff --git a/Makefile b/Makefile index 8e9120b..91389f9 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ help: ## Show issue core capability help @echo " issue-backend-list List configured backends" @echo " issue-backend-detect Auto-detect backend from repository" @echo " issue-backend-set-local Configure local SQLite backend" - @echo " issue-backend-set-gitea Configure Gitea backend" + @echo " issue-backend-set-gitea Configure Forgejo backend (Gitea-compatible API)" @echo "" @echo "Synchronization:" @echo " issue-sync Sync with remote backend" diff --git a/README.md b/README.md index 823c92e..67d39ed 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # 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.** +**A backend-agnostic connector to third-party issue trackers (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. +**issue-core** is a standardized abstraction layer for talking to external issue-tracking backends (Forgejo, 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. The self-hosted forge is **Forgejo** only (ACT-ADR-005); the HTTP API is Gitea-compatible. ### Fleet coordination vs external trackers @@ -32,7 +32,7 @@ For agent coordination *inside* the Coulomb fleet, use work records + state-hub, ✅ **Fully Implemented:** - Complete CRUD operations (issues, labels, users, milestones, comments) -- Gitea backend (production-ready with full API integration) +- Forgejo backend (Gitea-compatible API; production-ready) - Local SQLite backend (offline work with sync capability) - CLI with JSON output for machine parsing - Python API for programmatic access @@ -72,15 +72,20 @@ Clients authenticate with `Authorization: Bearer ` or `X-API-Key: `. See `SCOPE.md` "TaskSpec payload" for the request schema, or visit `http://:/docs` once the server is running for live OpenAPI docs. +Internal scheduled automation (FI/Binky, mail, …) does **not** poll this +server. Claim/execute is activity-core **`ops_run`** (ACT-ADR-005). +`POST /issues/` is external tracker create/link only. + ### Configuration (One-Time Setup) -**For Gitea-backed projects:** +**For Forgejo-backed projects:** ```bash -# Set your Gitea token -export GITEA_API_TOKEN="your-token-here" +# Preferred token name (CLI also accepts FORGEJO_API_TOKEN) +export FORGEJO_TOKEN="your-token-here" +# Deprecated aliases still work: GITEA_API_TOKEN, GITEA_TOKEN -# Configure backend +# Configure backend (type string `gitea` = Forgejo Gitea-compatible API) issue backend add myproject gitea # Prompts for: URL, owner, repo (reads token from environment) @@ -185,7 +190,7 @@ for issue in issues: ┌───────┴────────┐ │ │ ┌──────▼─────┐ ┌──────▼──────┐ -│ Local │ │ Gitea │ +│ Local │ │ Forgejo │ │ (SQLite) │ │ (REST API) │ └────────────┘ └─────────────┘ ``` @@ -200,7 +205,7 @@ for issue in issues: | Backend | Status | Features | |---------|--------|----------| -| **Gitea** | ✅ Production | Full API, rate limiting, state mapping | +| **Forgejo** (Gitea-compatible API) | ✅ 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 | @@ -209,7 +214,7 @@ for issue in issues: ### 1. External tracker ops (when a backend is in use) -Agents or humans work issues that already live in Gitea/GitHub (e.g. OSS +Agents or humans work issues that already live in Forgejo/GitHub (e.g. OSS inbound, customer Jira, or an intentional projection): ```bash @@ -227,7 +232,7 @@ Fleet-internal task claim/execute should use workplan task status in the repo ### 2. Human collaboration through a tracker UI -When the counterparty only uses GitHub/Gitea, project or update the external +When the counterparty only uses GitHub/Forgejo, 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`. @@ -238,14 +243,14 @@ Work offline with local backend, sync when online: ```bash # Setup local backup issue backend add backup local -issue sync pull gitea-production backup +issue sync pull forgejo-production backup # Work offline issue backend set-default backup issue create "Offline tracker note" --label=offline # Sync back -issue sync push backup gitea-production +issue sync push backup forgejo-production ``` ## CLI Commands Reference @@ -318,7 +323,7 @@ issue-core/ │ │ ├── models.py # Issue, Label, User, etc. │ │ └── interfaces.py # IssueBackend, SyncableBackend │ ├── backends/ -│ │ ├── gitea/ # Gitea backend implementation +│ │ ├── gitea/ # Forgejo backend (Gitea-compatible API) │ │ └── local/ # SQLite backend implementation │ └── cli/ # Click-based CLI │ ├── commands.py # Issue operations @@ -358,7 +363,7 @@ issue map detach --uuid ``` Mappings live in `~/.config/issue-tracker/mappings.db` (bookkeeping), independent -of whether CRUD targets Gitea or local SQLite. +of whether CRUD targets Forgejo or local SQLite. ## Roadmap (summary) @@ -412,7 +417,7 @@ The Issue Core is designed to be extensible: - [ ] Integration tests with mock API - [ ] Documentation -See existing backends (Gitea, Local) as reference implementations. +See existing backends (Forgejo / `gitea` module, Local) as reference implementations. ## Why "Facade" / connector? @@ -421,7 +426,7 @@ 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. +Instead of agents learning different APIs for GitHub (`gh`), GitLab (`glab`), Forgejo, 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 diff --git a/ROADMAP.md b/ROADMAP.md index 23635b6..f9935bc 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -28,7 +28,7 @@ replacing workplans/tasks. ✅ **Complete:** - Core CRUD operations (100%) -- Gitea backend (production-ready) +- Forgejo backend (Gitea-compatible API; production-ready) - Local SQLite backend (fully functional) - CLI with JSON output - Python programmatic API @@ -80,8 +80,8 @@ def detect_git_remote() -> Optional[Dict[str, str]]: Returns: { - 'platform': 'gitea' | 'github' | 'gitlab', - 'base_url': 'https://gitea.example.com', + 'platform': 'forgejo' | 'github' | 'gitlab', + 'base_url': 'https://forgejo.example.com', 'owner': 'myorg', 'repo': 'myproject' } @@ -90,15 +90,15 @@ def detect_git_remote() -> Optional[Dict[str, str]]: def parse_remote_url(url: str) -> Optional[Dict[str, str]]: """ Parse various git remote URL formats: - - https://gitea.example.com/owner/repo.git - - git@gitea.example.com:owner/repo.git + - https://forgejo.example.com/owner/repo.git + - git@forgejo.example.com:owner/repo.git - https://github.com/owner/repo """ ``` **Tests:** `tests/test_detection.py` - Test various URL formats (HTTPS, SSH, with/without .git) -- Test platform detection (Gitea, GitHub, GitLab) +- Test platform detection (Forgejo, GitHub, GitLab) - Test edge cases (subgroups, custom domains) **Effort:** 2-3 days @@ -112,7 +112,8 @@ def parse_remote_url(url: str) -> Optional[Dict[str, str]]: def load_backend_from_env() -> Optional[Dict[str, Any]]: """ Load backend config from environment variables: - - GITEA_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO + - FORGEJO_URL, FORGEJO_TOKEN, FORGEJO_OWNER, FORGEJO_REPO + (deprecated aliases: GITEA_URL, GITEA_TOKEN / GITEA_API_TOKEN, …) - GITHUB_TOKEN (with auto-detection) - GITLAB_URL, GITLAB_TOKEN """ @@ -149,10 +150,10 @@ issue config auto { "backend": { "type": "gitea", - "url": "https://gitea.example.com", + "url": "https://forgejo.example.com", "owner": "myorg", "repo": "myproject", - "token_source": "env:GITEA_TOKEN" // or "file:/path/to/token" + "token_source": "env:FORGEJO_TOKEN" // or "file:/path/to/token"; GITEA_* deprecated }, "sync": { "enabled": true, @@ -247,7 +248,7 @@ if issue backend show "$backend_name" &>/dev/null; then fi # Offer existing values as defaults - read -p "Gitea URL [$CURRENT_URL]: " url + read -p "Forgejo URL [$CURRENT_URL]: " url url="${url:-$CURRENT_URL}" read -p "Repository owner [$CURRENT_OWNER]: " owner @@ -403,7 +404,7 @@ class LockManager: **Storage:** Store claims in issue metadata or separate tracking table. -**For Gitea backend:** +**For Forgejo backend:** ```json // In issue.sync_metadata { @@ -788,7 +789,7 @@ def sync_with_strategy( ### Phase 1 Success - [ ] Agent can attach to any repo's tracker with zero manual config -- [ ] Environment-only setup works: `GITEA_TOKEN=xxx issue list` +- [ ] Environment-only setup works: `FORGEJO_TOKEN=xxx issue list` - [ ] Auto-detection accuracy: >95% for common platforms ### Phase 1.5 Success (mapping) diff --git a/ReusableCapabilitiesArchitecture.md b/ReusableCapabilitiesArchitecture.md index 3b1d2d2..413e463 100644 --- a/ReusableCapabilitiesArchitecture.md +++ b/ReusableCapabilitiesArchitecture.md @@ -55,7 +55,7 @@ A **Capability Implementation** is a concrete realization of a Capability Family **Characteristics:** - Provides **concrete functionality** (code, CLI, API) -- May implement **multiple backend variants** (Gitea, GitHub, local) +- May implement **multiple backend variants** (Forgejo, GitHub, local) - Has **maturity levels** (experimental, beta, production) - Can be **composed with other capabilities** @@ -255,7 +255,7 @@ metadata: implementation: issue-core version: 1.0.0 description: > - Unified interface for issue tracking across Gitea, GitHub, GitLab. + Unified interface for issue tracking across Forgejo, GitHub, GitLab. Enables agent coordination via standardized issue operations. purpose: @@ -315,7 +315,7 @@ purpose: - "Unified issue management for polyglot platform environments" problems_solved: - - "Direct API calls to GitHub/GitLab/Gitea (avoids credential sprawl)" + - "Direct API calls to GitHub/GitLab/Forgejo (avoids credential sprawl)" - "Inconsistent issue tracking access patterns" - "Platform-specific code in agents" - "Offline/online workflow synchronization" @@ -323,7 +323,7 @@ purpose: # When to use this capability usage_rules: MUST_USE_INSTEAD_OF: - - "Direct Gitea/GitHub/GitLab API calls" + - "Direct Forgejo/GitHub/GitLab API calls" - "Platform-specific CLIs (gh, glab)" - "Python libraries (PyGithub, python-gitlab)" @@ -361,7 +361,7 @@ integration: required: true method: manual # auto-detection planned for v1.1 steps: - - "Export GITEA_API_TOKEN environment variable" + - "Export FORGEJO_TOKEN environment variable (GITEA_API_TOKEN is a deprecated alias)" - "Run: issue backend add myproject gitea" - "Provide: URL, owner, repo when prompted" - "Run: issue backend set-default myproject" @@ -416,8 +416,8 @@ efficiency: credentials: method: environment_variables variables: - - GITEA_API_TOKEN - - GITEA_URL # optional with config + - FORGEJO_TOKEN # preferred; GITEA_API_TOKEN is a deprecated alias + - FORGEJO_URL # optional with config; GITEA_URL deprecated alias security: - "Tokens never in code or logs" @@ -736,7 +736,7 @@ metadata: ``` Project A needs GitHub issues Project B needs GitLab issues -Project C needs Gitea issues +Project C needs Forgejo issues → Pattern: "We need issue tracking" → Family: "issue-tracking" diff --git a/SCOPE.md b/SCOPE.md index d718a3e..8b891f1 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -25,7 +25,7 @@ aspirational lists when deciding “does issue-core already do X?” | Concept | Notes | | --- | --- | | `Issue` | id, number, title, description, state, timestamps, labels, assignees, milestone, comments, backend_id/type, `sync_metadata` | -| `IssueState` | `open`, `closed`, `in_progress`, `blocked` (Gitea maps non-closed → open on the wire) | +| `IssueState` | `open`, `closed`, `in_progress`, `blocked` (Forgejo Gitea-compatible API maps non-closed → open on the wire) | | `Label` / priority / type | Priority and type are primarily **label conventions** (`priority:high`, `bug`, …), not separate backend columns everywhere | | `User`, `Milestone`, `Comment` | First-class models on the backend interface | | `Priority`, `IssueType` enums | Helpers for label-derived classification | @@ -46,7 +46,7 @@ aspirational lists when deciding “does issue-core already do X?” | Backend | Module | Role | | --- | --- | --- | | **Local SQLite** | `issue_core.backends.local` | Offline store; hard delete supported; bulk ops; sync-capable | -| **Gitea** | `issue_core.backends.gitea` | Remote REST; **Forgejo-compatible** in railiance01 deploy; no true delete (close-as-delete); rate-limit aware; sync-capable | +| **Forgejo** | `issue_core.backends.gitea` | Remote REST via **Forgejo (Gitea-compatible API)**; backend type string remains `gitea`. Gitea is not a second product. No true delete (close-as-delete); rate-limit aware; sync-capable | **Not implemented as code:** GitHub, GitLab, Jira backends (optional deps may be declared in `pyproject.toml`; no backend package under `issue_core/backends/`). @@ -64,7 +64,9 @@ declared in `pyproject.toml`; no backend package under `issue_core/backends/`). - JSON-friendly output for agents (`--format=json` on list/show paths). - Backend configs: `~/.config/issue-tracker/` (default backend + named configs). - Mapping store: `~/.config/issue-tracker/mappings.db` (independent of CRUD backend). -- Gitea token typically from env (`GITEA_API_TOKEN` / config); never commit secrets. +- Forgejo token from env: **`FORGEJO_TOKEN`** (preferred) or `FORGEJO_API_TOKEN`; + legacy **`GITEA_API_TOKEN` / `GITEA_TOKEN`** are deprecated aliases the CLI + still accepts. Never commit secrets. ### 1.4b Work-record mapping (shipped v0.2.x) @@ -103,7 +105,7 @@ also `sync_metadata.mapping` and a row in `mappings.db`. ### 1.6 Synchronization (CLI) -- Bidirectional pull/push between configured backends (e.g. Gitea ↔ local). +- Bidirectional pull/push between configured backends (e.g. Forgejo ↔ local). - Uses `get_issues_modified_since` / conflict hooks where backends implement them. - Conflict handling is basic (operator / force flags) — not a full merge engine. @@ -111,7 +113,7 @@ also `sync_metadata.mapping` and a row in `mappings.db`. | Artifact | Provides | | --- | --- | -| PyPI package | Built wheel/sdist; Makefile targets publish to Coulomb Gitea/Forgejo registry | +| PyPI package | Built wheel/sdist; Makefile targets publish to Coulomb Forgejo registry | | Docker image | Built and published by this app repo; production consumes an immutable digest | | `rapp-issue-core` | Authoritative railiance01 runtime package: manifests, private Service, ExternalSecret references, NetworkPolicy, rollout, rollback, and live evidence | | `docs/package-release.md` | Release notes for packaging | @@ -148,7 +150,7 @@ Things this repo **owns** and may grow, consistent with the connector role. backends when tracker use is intentional. - List and filter (state, labels, assignee, milestone, text search as backends allow). - Label, assignee, and milestone management through the backend interface. -- Delete where the backend allows (SQLite hard delete; Gitea effectively close). +- Delete where the backend allows (SQLite hard delete; Forgejo effectively close). ### 2.2 Connector / mapping (owned direction) @@ -164,7 +166,9 @@ Things this repo **owns** and may grow, consistent with the connector role. ### 2.3 Surfaces - CLI for humans and agents on a shell. -- REST for intentional automation and worker claim/list/close. +- REST for intentional **external tracker** create/link and worker + list/update/close of tracker issues. This is **not** the fleet ops claim + queue (ACT-ADR-005 / activity-core `ops_run`). - Python library for embedding. - Optional future NATS consumer **only** for intentional external projection (never silent fleet work origin). @@ -204,6 +208,24 @@ activity-core (and peers) must not treat `POST /issues/` as the always-on default for every matched rule. Emitters own that policy; issue-core keeps accepting authenticated POSTs without advertising itself as a landing zone. Follow-up: `activity-core` **ACTIVITY-WP-0022** (from ISSUE-WP-0004-T05). +Default activity-core sink remains **state-hub progress + `ops_run`**, not +REST to issue-core for Binky/FI. + +### 3.3b Ops claim queue (ACT-ADR-005) — not this repo + +Internal scheduled automation **claim/execute** belongs to activity-core +**`ops_run`** ([ACT-ADR-005](../activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md)). +issue-core does **not** provide the fleet ops claim queue. Do not poll +`GET /issues/` as “the automation queue.” + +ACT-ADR-005: *issue-core’s correct role is a connector facade over +external trackers (Forgejo, GitHub, Jira, …). It is not the origin of +work records and not the default internal ops queue. Gitea is out of +scope for this fleet; the self-hosted forge is Forgejo.* + +`POST /issues/` = external tracker create/link only. rein-aharness +`poll --source=issue-core` is **legacy / external ticket** path only +(see ISSUE-WP-0006, REINAH-WP-0005-T06). ### 3.4 Project management @@ -246,8 +268,8 @@ Emitters record who/what spawned an external issue (e.g. activity-core | --- | --- | --- | | Human / agent shell | CLI | Tracker admin; `project` / `map` for work-record links | | Library consumers | Python API + `MappingService` | Same backends + mapping store without shell | -| Workers (e.g. harness) | REST GET/PATCH | List / claim / close external issues when in the loop | -| activity-core IssueSink | REST POST | **Optional, intentional** external issues only — not default for internal findings; may pass `work_record_uuid` | +| Workers (e.g. harness) | REST GET/PATCH | List / update / close **external tracker** issues when that tracker is in use — **not** the fleet ops claim queue | +| activity-core IssueSink | REST POST | **Optional, intentional** external issues only — not default for internal findings or Binky/FI; may pass `work_record_uuid`. Default sink is state-hub progress + `ops_run` (ACT-ADR-005) | | Mapping-aware emitters | CLI or POST `/issues/` with UUID | Project/link work-record UUID ↔ external id | ### 4.3 TaskSpec contract (`POST /issues/`) @@ -287,14 +309,16 @@ Retained for intentional emits and backward compatibility: ``` `issue_id` is the **backend** issue identity for the emitter’s log; not a -work-record UUID. (`github` appears in the response enum for forward -compatibility; no GitHub backend is shipped yet.) +work-record UUID. The wire value `gitea` is the Forgejo (Gitea-compatible +API) backend type string — not a second Gitea product. (`github` appears in +the response enum for forward compatibility; no GitHub backend is shipped +yet.) ### 4.4 Credential routing (operators / agents) | Need | Owner | | --- | --- | -| Gitea/Forgejo API token for backend | OpenBao / operator path (`warden route`) | +| Forgejo API token for backend (`FORGEJO_TOKEN`; legacy `GITEA_*` alias) | OpenBao / operator path (`warden route`) | | `ISSUE_CORE_API_KEY` for REST | Shared secret via deploy secrets (e.g. ExternalSecret) | | SSH certs | ops-warden only | @@ -309,7 +333,7 @@ issue_core/ core/ # models, IssueBackend ABC, factory, MappingService backends/ local/ # SQLite issue store - gitea/ # Gitea/Forgejo REST + gitea/ # Forgejo REST (Gitea-compatible API) cli/ # Click: issue, project, map, backend, sync, serve api/ # FastAPI: ingest + query (+ auth, schemas) tests/ @@ -326,7 +350,7 @@ tests, image construction, and Forgejo image publication. | Language | Python 3.8+ | | CLI | Click | | HTTP | FastAPI + Pydantic v2 + uvicorn (`[api]` extra) | -| HTTP client (Gitea) | requests | +| HTTP client (Forgejo) | requests | | Issue store (local) | SQLite (`issues.db`) | | Mapping store | SQLite (`mappings.db`, separate file) | | Tests | pytest | @@ -350,3 +374,5 @@ tests, image construction, and Forgejo image publication. (issue-core issues = external projections only) - `the-custodian/research/WorkOrchestrationArchitectureDraft.md` §4.2 - activity-core IssueSink / **ACTIVITY-WP-0022** — emitter-side default policy +- activity-core **ACT-ADR-005** — `ops_run` is the fleet ops claim queue; not issue-core +- `workplans/ISSUE-WP-0006-forgejo-only-projection-boundary.md` — Forgejo-only + projection boundary diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index e89a947..56c7eb7 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -2,9 +2,10 @@ # Render issue-core backends.json from environment, then start the API. # # The backend structure (host/owner/repo/default) is non-secret and supplied -# via the BACKENDS_TEMPLATE env (a ConfigMap), with the Gitea token injected -# from GITEA_BACKEND_TOKEN (an ExternalSecret-materialized Secret). The token -# is never baked into the image or committed to Git. +# via the BACKENDS_TEMPLATE env (a ConfigMap), with the Forgejo token injected +# from FORGEJO_BACKEND_TOKEN (preferred) or GITEA_BACKEND_TOKEN (deprecated +# alias; ExternalSecret-materialized Secret). The token is never baked into +# the image or committed to Git. set -eu CONFIG_DIR="${HOME}/.config/issue-tracker" @@ -14,12 +15,13 @@ mkdir -p "${CONFIG_DIR}" # Substitute the token placeholder using python (always present in the image) # to avoid shell-escaping issues with the secret value. +FORGEJO_BACKEND_TOKEN="${FORGEJO_BACKEND_TOKEN:-}" \ GITEA_BACKEND_TOKEN="${GITEA_BACKEND_TOKEN:-}" \ BACKENDS_TEMPLATE="${BACKENDS_TEMPLATE}" \ python - "${CONFIG_DIR}/backends.json" <<'PY' import json, os, sys tmpl = json.loads(os.environ["BACKENDS_TEMPLATE"]) -token = os.environ.get("GITEA_BACKEND_TOKEN", "") +token = os.environ.get("FORGEJO_BACKEND_TOKEN") or os.environ.get("GITEA_BACKEND_TOKEN", "") for cfg in tmpl.values(): if isinstance(cfg, dict) and cfg.get("token") == "__FROM_ENV__": cfg["token"] = token diff --git a/docs/boundary-sync-and-status-mapping.md b/docs/boundary-sync-and-status-mapping.md index 1bd9e64..b9f3659 100644 --- a/docs/boundary-sync-and-status-mapping.md +++ b/docs/boundary-sync-and-status-mapping.md @@ -21,7 +21,7 @@ | Side | Key | | --- | --- | | Work record | **UUIDv7** (bookkeeping); canonical id optional denorm for UX | -| External | `(backend, external_id)` where backend is `sqlite` \| `gitea` \| … | +| External | `(backend, external_id)` where backend is `sqlite` \| `gitea` (Forgejo) \| … | ## Outward status table (task kind + pass-through) diff --git a/docs/intent-work-record-alignment-review.md b/docs/intent-work-record-alignment-review.md index e073b92..c1f463c 100644 --- a/docs/intent-work-record-alignment-review.md +++ b/docs/intent-work-record-alignment-review.md @@ -87,7 +87,7 @@ without naming UUIDv7 or the human-facing id. **Why it matters:** Mapping implementation and CLI (`issue project ISSUE-WP-…`) will almost always start from the **canonical name**; storage key should be UUID. INTENT’s UUID-only phrasing can steer readers to hide -canonical ids or treat Gitea numbers as peer identity to human task names. +canonical ids or treat Forgejo numbers as peer identity to human task names. **Recommendation:** In INTENT “Why / Mapping”, say: @@ -215,7 +215,7 @@ Framework makes `lane` mandatory on every work record; tags and budgets are normative. INTENT is silent — **acceptable** if projections never invent parallel -lane/budget models on issues. Soft risk if Gitea labels start carrying a +lane/budget models on issues. Soft risk if Forgejo labels start carrying a second autonomy model (`lane:green` as issue labels without mapping rules). **Recommendation:** Mapping/projection: lane, tags, budget stay on the work @@ -231,7 +231,7 @@ INTENT’s “What it is” lists responsibilities that mix **shipped** and | Responsibility in INTENT | Shipped? | | --- | --- | -| Projection / CRUD on backends | **Yes** (SQLite, Gitea) | +| Projection / CRUD on backends | **Yes** (SQLite, Forgejo) | | Mapping UUID ↔ external id | **No** — design only | | Two-way boundary sync | **No** — CLI backend sync ≠ work-record boundary sync | | CLI + REST | **Yes** | @@ -295,7 +295,7 @@ defaults, rewriting AGENT_INTEGRATION examples — those are separate work. | Connector role | Yes | Yes | Yes | | Not work origin | Yes | §3.1 | Yes | | Mapping | Target | Design + not shipped | Yes if INTENT softens “What it is” | -| Backends | SQLite + Gitea (+ planned) | Same | Yes | +| Backends | SQLite + Forgejo (+ planned) | Same | Yes | | REST TaskSpec | Optional intentional | Full contract | Yes | | State-hub runtime events from package | Downstream mention | Explicitly not shipped | Mild aspirational gap in older SCOPE; current SCOPE honest | diff --git a/docs/package-release.md b/docs/package-release.md index 52cee67..ca35904 100644 --- a/docs/package-release.md +++ b/docs/package-release.md @@ -15,7 +15,7 @@ make package-check Publish to the Coulomb organization registry: ```bash -TWINE_USERNAME= \ +TWINE_USERNAME= \ TWINE_PASSWORD= \ make publish-forgejo ``` diff --git a/docs/uuid-external-id-mapping.md b/docs/uuid-external-id-mapping.md index 8bc525d..b9de6e2 100644 --- a/docs/uuid-external-id-mapping.md +++ b/docs/uuid-external-id-mapping.md @@ -11,13 +11,13 @@ kinds in `the-custodian/canon/standards/work-record-types_v0.1.md` ## Problem -When collaboration requires a third-party tracker (Forgejo/Gitea, GitHub, +When collaboration requires a third-party tracker (Forgejo, GitHub, Jira, …), the fleet still needs a stable link between: | Side | Identity today | Owner | | --- | --- | --- | | Internal work record | UUIDv7 written back as `state_hub_*_id` (workplan/task/intake/…) | repo file + state-hub | -| External issue | Backend issue id / number (+ URL) | Gitea, GitHub, … via issue-core | +| External issue | Backend issue id / number (+ URL) | Forgejo, GitHub, … via issue-core | Without an explicit mapping: @@ -95,7 +95,7 @@ mapping: work_record_uuid: # primary internal key work_record_id: "ISSUE-WP-0004-T01" # canonical name, optional denorm work_record_kind: task # task | intake | workplan | … - backend: gitea # gitea | github | gitlab | jira | sqlite + backend: gitea # forgejo product; type string `gitea` | github | gitlab | jira | sqlite external_id: "176" # backend-native issue id/number external_url: "https://…/issues/176" target_repo: "coulomb/example" # optional routing hint @@ -123,7 +123,7 @@ issue-core only: ```yaml # on a task / intake YAML block (illustrative — exact key names TBD with canon) external_tracker: - backend: gitea + backend: forgejo # product; stored type may be `gitea` issue_id: "176" issue_url: "https://…" mapped_by: issue-core @@ -132,7 +132,7 @@ external_tracker: Alternatively a single string field if canon prefers flat keys: ```text -issue_core_external_ref: "gitea:coulomb/example#176" +issue_core_external_ref: "forgejo:coulomb/example#176" ``` Canon PR owns the field name; issue-core owns the mapping authority. @@ -142,7 +142,7 @@ Canon PR owns the field name; issue-core owns the mapping authority. Recommended v1: 1. **Local SQLite** table `work_record_issue_map` (even when primary CRUD - backend is Gitea) — mapping is fleet bookkeeping, not a Gitea concept. + backend is Forgejo) — mapping is fleet bookkeeping, not a Forgejo concept. 2. **Issue.sync_metadata.mapping** (or top-level) echo for convenience when the issue is loaded: ```json @@ -154,7 +154,7 @@ Recommended v1: } } ``` -3. Optional Gitea label / body footer for human visibility in the tracker UI +3. Optional Forgejo label / body footer for human visibility in the tracker UI (non-authoritative). ### API / CLI (sketch only) @@ -255,7 +255,7 @@ increment. Until enabled, connector load is zero beyond idle process cost. documenting multi-tracker projection is future work. - **SQLite-only offline:** mapping table lives with local DB; sync of issues and mappings to remote is coordinated (mapping rows are not - pushed as Gitea native objects). + pushed as Forgejo native objects). ## Implementation sketch (later workplan) diff --git a/examples/agents/README.md b/examples/agents/README.md index 6798ade..576267a 100644 --- a/examples/agents/README.md +++ b/examples/agents/README.md @@ -12,7 +12,8 @@ This directory contains working examples of autonomous agents using the Issue Co 2. **Configure backend** (one-time setup): ```bash - export GITEA_API_TOKEN="your-token" + export FORGEJO_TOKEN="your-token" + # Deprecated alias: GITEA_API_TOKEN issue backend add myproject gitea # Enter: URL, owner, repo when prompted issue backend set-default myproject @@ -20,10 +21,11 @@ This directory contains working examples of autonomous agents using the Issue Co 3. **Set environment variables** for scripts: ```bash - export GITEA_URL=https://gitea.example.com - export GITEA_TOKEN=your-token - export GITEA_OWNER=your-org - export GITEA_REPO=your-repo + export FORGEJO_URL=https://forgejo.example.com + export FORGEJO_TOKEN=your-token + export FORGEJO_OWNER=your-org + export FORGEJO_REPO=your-repo + # Deprecated aliases: GITEA_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO ``` ## Examples @@ -212,7 +214,7 @@ issue backend add myproject gitea ### "Authentication failed" ```bash # Check token is valid -curl -H "Authorization: token $GITEA_TOKEN" $GITEA_URL/api/v1/user +curl -H "Authorization: token $FORGEJO_TOKEN" $FORGEJO_URL/api/v1/user ``` ### "No issues found" diff --git a/examples/agents/human_in_loop.py b/examples/agents/human_in_loop.py index dddd0de..f4b4236 100755 --- a/examples/agents/human_in_loop.py +++ b/examples/agents/human_in_loop.py @@ -9,10 +9,10 @@ This agent demonstrates agent-human collaboration: 4. Agent can ask questions and wait for answers Usage: - export GITEA_URL=https://gitea.example.com - export GITEA_TOKEN=your-token - export GITEA_OWNER=your-org - export GITEA_REPO=your-repo + export FORGEJO_URL=https://forgejo.example.com + export FORGEJO_TOKEN=your-token + export FORGEJO_OWNER=your-org + export FORGEJO_REPO=your-repo python human_in_loop.py """ @@ -40,11 +40,11 @@ class HumanInLoopAgent: self.backend = None def connect(self): - """Connect to backend.""" - base_url = os.environ['GITEA_URL'] - token = os.environ['GITEA_TOKEN'] - owner = os.environ['GITEA_OWNER'] - repo = os.environ['GITEA_REPO'] + """Connect to Forgejo backend.""" + 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_TOKEN'] + owner = os.environ.get('FORGEJO_OWNER') or os.environ['GITEA_OWNER'] + repo = os.environ.get('FORGEJO_REPO') or os.environ['GITEA_REPO'] self.backend = GiteaBackend() self.backend.connect({ diff --git a/examples/agents/monitoring_agent.py b/examples/agents/monitoring_agent.py index 719f6ea..62525e4 100755 --- a/examples/agents/monitoring_agent.py +++ b/examples/agents/monitoring_agent.py @@ -9,10 +9,10 @@ This agent monitors issue health and sends alerts: - Reports on project velocity and bottlenecks Usage: - export GITEA_URL=https://gitea.example.com - export GITEA_TOKEN=your-token - export GITEA_OWNER=your-org - export GITEA_REPO=your-repo + export FORGEJO_URL=https://forgejo.example.com + export FORGEJO_TOKEN=your-token + export FORGEJO_OWNER=your-org + export FORGEJO_REPO=your-repo python monitoring_agent.py [--stale-days=7] [--check-interval=3600] """ @@ -42,11 +42,11 @@ class MonitoringAgent: self.backend = None def connect(self): - """Connect to backend.""" - base_url = os.environ['GITEA_URL'] - token = os.environ['GITEA_TOKEN'] - owner = os.environ['GITEA_OWNER'] - repo = os.environ['GITEA_REPO'] + """Connect to Forgejo backend.""" + 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_TOKEN'] + owner = os.environ.get('FORGEJO_OWNER') or os.environ['GITEA_OWNER'] + repo = os.environ.get('FORGEJO_REPO') or os.environ['GITEA_REPO'] self.backend = GiteaBackend() self.backend.connect({ diff --git a/examples/agents/multi_agent_pipeline.py b/examples/agents/multi_agent_pipeline.py index ecdebda..7753d04 100755 --- a/examples/agents/multi_agent_pipeline.py +++ b/examples/agents/multi_agent_pipeline.py @@ -11,10 +11,10 @@ Demonstrates a CI/CD-like pipeline with multiple specialized agents: Each agent monitors for issues in their stage and advances them through the pipeline. Usage: - export GITEA_URL=https://gitea.example.com - export GITEA_TOKEN=your-token - export GITEA_OWNER=your-org - export GITEA_REPO=your-repo + export FORGEJO_URL=https://forgejo.example.com + export FORGEJO_TOKEN=your-token + export FORGEJO_OWNER=your-org + export FORGEJO_REPO=your-repo # Run all agents in parallel (in separate terminals) python multi_agent_pipeline.py --agent=coder @@ -50,11 +50,11 @@ class BaseAgent: self.backend = None def connect(self): - """Connect to backend from environment.""" - base_url = os.environ['GITEA_URL'] - token = os.environ['GITEA_TOKEN'] - owner = os.environ['GITEA_OWNER'] - repo = os.environ['GITEA_REPO'] + """Connect to Forgejo backend from environment.""" + 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_TOKEN'] + owner = os.environ.get('FORGEJO_OWNER') or os.environ['GITEA_OWNER'] + repo = os.environ.get('FORGEJO_REPO') or os.environ['GITEA_REPO'] self.backend = GiteaBackend() self.backend.connect({ diff --git a/examples/agents/simple_task_executor.py b/examples/agents/simple_task_executor.py index 121db62..fdd9815 100755 --- a/examples/agents/simple_task_executor.py +++ b/examples/agents/simple_task_executor.py @@ -9,10 +9,10 @@ This agent demonstrates a basic workflow: 4. Report completion and close the issue Usage: - export GITEA_URL=https://gitea.example.com - export GITEA_TOKEN=your-token - export GITEA_OWNER=your-org - export GITEA_REPO=your-repo + export FORGEJO_URL=https://forgejo.example.com + export FORGEJO_TOKEN=your-token + export FORGEJO_OWNER=your-org + export FORGEJO_REPO=your-repo python simple_task_executor.py """ @@ -39,16 +39,22 @@ class SimpleTaskExecutor: self.backend = None def connect(self): - """Connect to Gitea backend from environment variables.""" - base_url = os.environ.get('GITEA_URL') - token = os.environ.get('GITEA_TOKEN') - owner = os.environ.get('GITEA_OWNER') - repo = os.environ.get('GITEA_REPO') + """Connect to Forgejo backend from environment variables.""" + base_url = os.environ.get('FORGEJO_URL') or os.environ.get('GITEA_URL') + token = ( + os.environ.get('FORGEJO_TOKEN') + or os.environ.get('FORGEJO_API_TOKEN') + or os.environ.get('GITEA_TOKEN') + or os.environ.get('GITEA_API_TOKEN') + ) + owner = os.environ.get('FORGEJO_OWNER') or os.environ.get('GITEA_OWNER') + repo = os.environ.get('FORGEJO_REPO') or os.environ.get('GITEA_REPO') if not all([base_url, token, owner, repo]): raise ValueError( "Missing required environment variables: " - "GITEA_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO" + "FORGEJO_URL, FORGEJO_TOKEN, FORGEJO_OWNER, FORGEJO_REPO " + "(deprecated aliases: GITEA_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO)" ) self.backend = GiteaBackend() diff --git a/integration/gitea-backend.integration.yaml b/integration/gitea-backend.integration.yaml index 991795c..75ed002 100644 --- a/integration/gitea-backend.integration.yaml +++ b/integration/gitea-backend.integration.yaml @@ -1,9 +1,10 @@ schema_version: open-reuse.integration.v0.1 id: issue-core-gitea -name: issue-core Gitea Backend +name: issue-core Forgejo Backend description: > Pluggable remote backend that maps the issue-core unified issue model onto the - Gitea issues API for external-tracker projection and synchronization. + Forgejo issues API (Gitea-compatible) for external-tracker projection and + synchronization. Gitea is not a second supported product. status: registered owner: issue-core @@ -13,9 +14,9 @@ local: system: issue-core upstream: - name: Gitea - project_url: https://github.com/go-gitea/gitea - homepage: https://about.gitea.com/ + name: Forgejo (Gitea-compatible API) + project_url: https://codeberg.org/forgejo/forgejo + homepage: https://forgejo.org/ version_policy: gitea-api-v1 monitor: releases: true @@ -29,14 +30,14 @@ reuse: - plugin risk_level: medium rationale: > - Gitea REST API is wrapped behind the RemoteBackend interface; local task - lifecycle semantics remain stable across backend swaps. + Forgejo (Gitea-compatible) REST API is wrapped behind the RemoteBackend + interface; local task lifecycle semantics remain stable across backend swaps. boundary: type: adapter local_adapter: issue_core.backends.gitea.backend.GiteaBackend local_interface: issue_core.core.interfaces.RemoteBackend - reused_surface: Gitea /api/v1 issues, labels, milestones, comments + reused_surface: Forgejo (Gitea-compatible) /api/v1 issues, labels, milestones, comments contracts: - issue-core.backend.v1 fragility_points: diff --git a/issue_core/__init__.py b/issue_core/__init__.py index 911fd17..f80d45e 100644 --- a/issue_core/__init__.py +++ b/issue_core/__init__.py @@ -2,12 +2,12 @@ issue-core — external issue-tracker connector Backend-agnostic CLI, library, and optional REST surface for operating on -third-party issue trackers (Gitea/Forgejo, local SQLite). Not the origin of -fleet work records (see INTENT.md / SCOPE.md). +third-party issue trackers (Forgejo via Gitea-compatible API, local SQLite). +Not the origin of fleet work records (see INTENT.md / SCOPE.md). Shipped: - Unified issue model and IssueBackend plugin architecture -- Local SQLite + Gitea backends; bidirectional sync CLI +- Local SQLite + Forgejo backends; bidirectional sync CLI - CLI (`issue` / `issue-core`) and optional FastAPI REST (`issue serve`) """ diff --git a/issue_core/backends/__init__.py b/issue_core/backends/__init__.py index 429769c..d9da69e 100644 --- a/issue_core/backends/__init__.py +++ b/issue_core/backends/__init__.py @@ -7,5 +7,5 @@ API regardless of the underlying issue tracking system. Available Backends: - local: SQLite-based local backend for offline use -- gitea: Gitea API backend for GitHub-compatible systems +- gitea: Forgejo backend (Gitea-compatible API) """ \ No newline at end of file diff --git a/issue_core/backends/gitea/__init__.py b/issue_core/backends/gitea/__init__.py index c02af71..b00ae63 100644 --- a/issue_core/backends/gitea/__init__.py +++ b/issue_core/backends/gitea/__init__.py @@ -1,12 +1,11 @@ """ -Gitea Backend +Forgejo backend (Gitea-compatible API). -A backend implementation for Gitea issue tracking systems. -This backend provides integration with Gitea API for remote issue management. +Talks to the self-hosted Forgejo HTTP API. Gitea is not a second supported +product. The backend type string remains `gitea`. Features: -- Full Gitea API integration -- GitHub-compatible operations +- Full Forgejo / Gitea-compatible API integration - Remote synchronization - Authentication support - Rate limiting compliance diff --git a/issue_core/backends/gitea/backend.py b/issue_core/backends/gitea/backend.py index 4ed4b15..3aa73d9 100644 --- a/issue_core/backends/gitea/backend.py +++ b/issue_core/backends/gitea/backend.py @@ -1,8 +1,9 @@ """ -Gitea Backend Implementation +Forgejo backend (Gitea-compatible API). -Provides integration with Gitea API for remote issue tracking. -This backend adapts the Gitea API to our unified issue model. +Talks to the self-hosted Forgejo HTTP API, which is Gitea-compatible. +Gitea is not a second supported product. The backend type string remains +`gitea` for compatibility. """ import requests @@ -16,7 +17,7 @@ from ...core.models import Issue, Label, User, Milestone, Comment, IssueState, P class GiteaAPIError(Exception): - """Gitea API specific errors.""" + """Forgejo (Gitea-compatible API) errors.""" pass @@ -26,7 +27,7 @@ class GiteaRateLimitError(GiteaAPIError): class GiteaBackend(RemoteBackend, SyncableBackend): - """Gitea API backend for remote issue tracking.""" + """Forgejo backend using the Gitea-compatible HTTP API.""" def __init__(self): self.base_url: Optional[str] = None @@ -44,7 +45,7 @@ class GiteaBackend(RemoteBackend, SyncableBackend): supports_webhooks=True, supports_real_time=False, max_labels_per_issue=None, - max_assignees_per_issue=10 # Gitea typical limit + max_assignees_per_issue=10 # Forgejo / Gitea-compatible typical limit ) @property @@ -56,7 +57,7 @@ class GiteaBackend(RemoteBackend, SyncableBackend): return self._capabilities def connect(self, config: Dict[str, Any]) -> None: - """Connect to Gitea API.""" + """Connect to Forgejo (Gitea-compatible API).""" self.base_url = config['base_url'].rstrip('/') self.token = config['token'] self.owner = config['owner'] @@ -71,10 +72,10 @@ class GiteaBackend(RemoteBackend, SyncableBackend): # Test connection if not self.test_connection(): - raise GiteaAPIError("Failed to connect to Gitea API") + raise GiteaAPIError("Failed to connect to Forgejo (Gitea-compatible API)") def disconnect(self) -> None: - """Disconnect from Gitea API.""" + """Disconnect from Forgejo.""" self.session.close() self.base_url = None self.token = None @@ -195,20 +196,20 @@ class GiteaBackend(RemoteBackend, SyncableBackend): if issue.milestone: data['milestone'] = int(issue.milestone.backend_id) if issue.milestone.backend_id else None - # Gitea expects numeric label IDs on issue create/update. Name-only - # labels are preserved in issue-core metadata but omitted from the API - # payload until a label-resolution step exists. - label_ids = [] - for label in issue.labels: - if not label.backend_id: - continue - try: - label_ids.append(int(label.backend_id)) - except (TypeError, ValueError): - continue - if label_ids: - data['labels'] = label_ids - + # Gitea expects numeric label IDs on issue create/update. Name-only + # labels are preserved in issue-core metadata but omitted from the API + # payload until a label-resolution step exists. + label_ids = [] + for label in issue.labels: + if not label.backend_id: + continue + try: + label_ids.append(int(label.backend_id)) + except (TypeError, ValueError): + continue + if label_ids: + data['labels'] = label_ids + return data # Issue CRUD Operations diff --git a/issue_core/cli/backend_commands.py b/issue_core/cli/backend_commands.py index 1adaf80..6c41da1 100644 --- a/issue_core/cli/backend_commands.py +++ b/issue_core/cli/backend_commands.py @@ -12,6 +12,23 @@ from .utils import ( echo_error, echo_warning, echo_info, confirm_action ) +# Preferred FORGEJO_*; GITEA_* remain deprecated aliases (ISSUE-WP-0006). +_FORGE_TOKEN_ENV_NAMES = ( + 'FORGEJO_TOKEN', + 'FORGEJO_API_TOKEN', + 'GITEA_API_TOKEN', + 'GITEA_TOKEN', +) + + +def _forge_token_from_env(): + """Return (env_name, token) for the first set Forgejo/legacy token var.""" + for name in _FORGE_TOKEN_ENV_NAMES: + value = os.getenv(name) + if value: + return name, value + return None, None + @click.group() def backend_group(): @@ -46,14 +63,19 @@ def add_backend(ctx, name, backend_type): 'db_path': str(db_path) } elif backend_type == 'gitea': - base_url = click.prompt('Gitea base URL (e.g., https://git.example.com)') + base_url = click.prompt('Forgejo base URL (Gitea-compatible API)') owner = click.prompt('Repository owner/organization') repo = click.prompt('Repository name') - # Check for API token in environment variable first - env_token = os.getenv('GITEA_API_TOKEN') + env_name, env_token = _forge_token_from_env() if env_token: - click.echo(f"Using API token from GITEA_API_TOKEN environment variable") + if env_name in ('GITEA_API_TOKEN', 'GITEA_TOKEN'): + click.echo( + f"Using API token from {env_name} " + "(deprecated alias; prefer FORGEJO_TOKEN)" + ) + else: + click.echo(f"Using API token from {env_name} environment variable") token = env_token else: token = click.prompt('Access token', hide_input=True) diff --git a/pyproject.toml b/pyproject.toml index 28222d1..7da4946 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "issue-core" -description = "External issue-tracker connector (Gitea/Forgejo, SQLite) with work-record UUID mapping — not the fleet work origin" +description = "External issue-tracker connector (Forgejo, SQLite) with work-record UUID mapping — not the fleet work origin" readme = "README.md" requires-python = ">=3.8" license = {text = "MIT"} diff --git a/registry/capabilities/capability.infotech.issue-tracking.md b/registry/capabilities/capability.infotech.issue-tracking.md index 67ebd67..bc2ccb3 100644 --- a/registry/capabilities/capability.infotech.issue-tracking.md +++ b/registry/capabilities/capability.infotech.issue-tracking.md @@ -1,7 +1,7 @@ --- id: capability.infotech.issue-tracking name: External Issue Tracker Connector -summary: Backend-agnostic CLI/Python connector to Gitea/Forgejo (and SQLite cache) +summary: Backend-agnostic CLI/Python connector to Forgejo (and SQLite cache) with work-record UUID ↔ external issue mapping. Not the fleet work origin. owner: issue-core status: draft @@ -29,10 +29,10 @@ external_evidence: confidence: medium basis: scope_vs_intent_and_consumer_expectations satisfied_expectations: - - Gitea + local SQLite backends + - Forgejo + local SQLite backends - CLI with JSON output; project/map commands - work_record_uuid optional on TaskSpec - - credential handling via env vars (GITEA_API_TOKEN, ISSUE_CORE_API_KEY) + - credential handling via env vars (FORGEJO_TOKEN preferred; GITEA_API_TOKEN deprecated alias; ISSUE_CORE_API_KEY) broken_expectations: [] out_of_scope_expectations: - Fleet task origin / intake promotion (work-record canon) diff --git a/registry/indexes/capabilities.yaml b/registry/indexes/capabilities.yaml index f2d9de6..7be3fce 100644 --- a/registry/indexes/capabilities.yaml +++ b/registry/indexes/capabilities.yaml @@ -4,7 +4,7 @@ domain: helix_forge capabilities: - id: capability.infotech.issue-tracking name: Universal Issue Tracking Coordination - summary: Unified Python/CLI interface for issue tracking across Gitea, GitHub, and GitLab, preventing + summary: Unified Python/CLI interface for issue tracking across Forgejo, GitHub, and GitLab, preventing direct platform API usage and credential sprawl for coordinating agents. vector: D4 / A2 / C2 / R1 domain: infotech diff --git a/tests/test_cli_commands.py b/tests/test_cli_commands.py index a7e57d3..38bd2b4 100644 --- a/tests/test_cli_commands.py +++ b/tests/test_cli_commands.py @@ -53,7 +53,7 @@ class TestCLICommands: ], input='https://git.example.com\ntestorg\ntestrepo\n') assert result.exit_code == 0 - assert 'Using API token from GITEA_API_TOKEN environment variable' in result.output + assert 'Using API token from FORGEJO_TOKEN environment variable' in result.output assert 'Backend \'test-gitea\' added successfully' in result.output # Verify save_backend_configs was called with correct data @@ -205,8 +205,13 @@ class TestEnvironmentTokenDetection: @patch('os.getenv') def test_gitea_token_detection(self, mock_getenv): - """Test GITEA_API_TOKEN environment variable detection.""" - mock_getenv.return_value = 'test-env-token' + """Deprecated GITEA_API_TOKEN alias is still accepted.""" + def _getenv(name, default=None): + if name == 'GITEA_API_TOKEN': + return 'test-env-token' + return default + + mock_getenv.side_effect = _getenv from issue_core.cli.backend_commands import add_backend @@ -220,4 +225,5 @@ class TestEnvironmentTokenDetection: ], input='https://git.example.com\ntestorg\ntestrepo\n') assert result.exit_code == 0 - assert 'Using API token from GITEA_API_TOKEN environment variable' in result.output \ No newline at end of file + assert 'Using API token from GITEA_API_TOKEN' in result.output + assert 'deprecated alias; prefer FORGEJO_TOKEN' in result.output \ No newline at end of file diff --git a/tests/test_gitea_backend.py b/tests/test_gitea_backend.py index e3107c8..fd67a34 100644 --- a/tests/test_gitea_backend.py +++ b/tests/test_gitea_backend.py @@ -77,7 +77,7 @@ class TestGiteaBackend: backend = GiteaBackend() backend.session = mock_session - with pytest.raises(GiteaAPIError, match="Failed to connect to Gitea API"): + with pytest.raises(GiteaAPIError, match="Failed to connect to Forgejo"): backend.connect(self.test_config) def test_url_construction_fix(self): @@ -98,29 +98,29 @@ class TestGiteaBackend: called_url = mock_request.call_args[1]['url'] if 'url' in mock_request.call_args[1] else mock_request.call_args[0][1] assert called_url == 'https://git.example.com/api/v1/repos/owner/repo' - def test_gitea_payload_omits_name_only_labels(self): - """Gitea issue payloads only include numeric label IDs.""" - now = datetime.now(timezone.utc) - issue = Issue( - id="", - number=0, - title="Test issue", - description="Test description", - state=IssueState.OPEN, - created_at=now, - updated_at=now, - labels=[ - Label(name="priority:low"), - Label(name="source:rule", backend_id="not-a-number"), - Label(name="existing", backend_id="42"), - ], - ) - - payload = self.backend._unified_issue_to_gitea(issue) - - assert payload["labels"] == [42] - assert payload["title"] == "Test issue" - + def test_gitea_payload_omits_name_only_labels(self): + """Gitea issue payloads only include numeric label IDs.""" + now = datetime.now(timezone.utc) + issue = Issue( + id="", + number=0, + title="Test issue", + description="Test description", + state=IssueState.OPEN, + created_at=now, + updated_at=now, + labels=[ + Label(name="priority:low"), + Label(name="source:rule", backend_id="not-a-number"), + Label(name="existing", backend_id="42"), + ], + ) + + payload = self.backend._unified_issue_to_gitea(issue) + + assert payload["labels"] == [42] + assert payload["title"] == "Test issue" + @patch('issue_core.backends.gitea.backend.requests.Session') def test_test_connection_success(self, mock_session_class): """Test test_connection method works correctly.""" diff --git a/workplans/ISSUE-WP-0004-align-with-work-record-canon.md b/workplans/ISSUE-WP-0004-align-with-work-record-canon.md index 406be9e..1aa53d5 100644 --- a/workplans/ISSUE-WP-0004-align-with-work-record-canon.md +++ b/workplans/ISSUE-WP-0004-align-with-work-record-canon.md @@ -49,8 +49,8 @@ be an alternative origin. This workplan brings issue-core's own docs, scope, and (where cheap) code in line with that decision. It does not remove issue-core's existing -capability — Gitea/GitHub backend CRUD stays useful — it retargets *what -issue-core is for*. +capability — Forgejo/GitHub backend CRUD stays useful — it retargets *what +issue-core is for*. (Product language: Forgejo only; see ISSUE-WP-0006.) ## Task: Rewrite INTENT.md — connector, not landing zone diff --git a/workplans/ISSUE-WP-0005-connector-alignment-implementation.md b/workplans/ISSUE-WP-0005-connector-alignment-implementation.md index 6fd9c30..448f886 100644 --- a/workplans/ISSUE-WP-0005-connector-alignment-implementation.md +++ b/workplans/ISSUE-WP-0005-connector-alignment-implementation.md @@ -35,7 +35,7 @@ Close the gap between: | Layer | State at start of this WP | | --- | --- | | **INTENT** | Work-record-aligned connector (2026-07-22) | -| **SCOPE §1 shipped** | Models, SQLite/Gitea, CLI, REST TaskSpec, backend sync | +| **SCOPE §1 shipped** | Models, SQLite/Forgejo, CLI, REST TaskSpec, backend sync | | **SCOPE §1.8 / target** | Mapping store/API, boundary sync, work_record_uuid, dual-lifecycle rules | Deliver a **minimum viable connector core**: durable mapping + project/link @@ -137,14 +137,14 @@ state_hub_task_id: "027871b0-696d-5ab7-b18c-b26167e97813" Per `docs/uuid-external-id-mapping.md`: - Table `work_record_issue_map` (or equivalent) on local backend / dedicated - store used even when CRUD backend is Gitea + store used even when CRUD backend is Forgejo - Uniqueness: active `(backend, external_id)` and active `(work_record_uuid, backend)` - Domain service `MappingService`: upsert, resolve by UUID, by canonical id (if provided), by external id, detach - Unit tests for idempotent project and uniqueness -**Acceptance:** Tests pass without a live Gitea; store survives reconnect; +**Acceptance:** Tests pass without a live Forgejo; store survives reconnect; SCOPE §1.8 “mapping store” can move toward shipped with an honest partial note until CLI/API land. @@ -208,7 +208,7 @@ Implement **outward-only** application of T03 rules for mapped records - Do **not** write work-record files - Inward sync remains out of scope for this task (document as next) -**Acceptance:** Policy tests + one integration-style test on local/Gitea mock; +**Acceptance:** Policy tests + one integration-style test on local/Forgejo mock; SCOPE distinguishes backend sync vs work-record boundary sync clearly. ## Task: SCOPE inventory refresh after implementation diff --git a/workplans/ISSUE-WP-0006-forgejo-only-projection-boundary.md b/workplans/ISSUE-WP-0006-forgejo-only-projection-boundary.md index e59abc4..c21b802 100644 --- a/workplans/ISSUE-WP-0006-forgejo-only-projection-boundary.md +++ b/workplans/ISSUE-WP-0006-forgejo-only-projection-boundary.md @@ -4,18 +4,20 @@ type: workplan title: "Forgejo-only forge + projection boundary (not ops queue)" domain: infotech repo: issue-core -status: ready +status: finished owner: grok topic_slug: issue-core priority: medium created: "2026-08-03" -updated: "2026-08-03" +updated: "2026-09-14" depends_on: [] related: - ACT-ADR-005 - ACTIVITY-WP-0022 - ISSUE-WP-0004 - ISSUE-WP-0005 + - REIN-A-0002 + - REINAH-WP-0005 state_hub_workstream_id: "4d465264-cbe1-56ba-84ed-bd580b649b76" --- @@ -41,7 +43,7 @@ and so rein-aharness stops treating issue-core as the primary automation queue ```task id: ISSUE-WP-0006-T01 -status: todo +status: done priority: high state_hub_task_id: "927eee42-e22d-5c0d-a4f8-dcd36c53c8e9" ``` @@ -65,7 +67,7 @@ compatibility notes remain. ```task id: ISSUE-WP-0006-T02 -status: todo +status: done priority: high state_hub_task_id: "a1d69a48-833b-5e5e-8286-14f5aba627f3" ``` @@ -87,7 +89,7 @@ Explicit section: ```task id: ISSUE-WP-0006-T03 -status: todo +status: done priority: medium state_hub_task_id: "75eef8a3-abd7-50dd-9171-3488fda84da5" ``` @@ -107,7 +109,7 @@ as the primary automation loop. ```task id: ISSUE-WP-0006-T04 -status: todo +status: done priority: low state_hub_task_id: "5be0bce4-1816-599f-a98b-4fa1565a8b0e" ``` @@ -117,14 +119,32 @@ No code change required in issue-core if rein owns the doc cut. **Done when:** cross-link in ISSUE-WP-0006 and REIN-A-0002. +**Closed 2026-09-14:** REIN-A-0002 was completed as **REINAH-WP-0005**. T06 +there already marked issue-core poll as legacy/external. Cross-links added: + +- this workplan `related: REIN-A-0002`, `REINAH-WP-0005` +- `~/rein-aharness/workplans/REINAH-WP-0005-ops-run-claim-loop.md` T06 note + + `related: ISSUE-WP-0006` +- `~/rein-aharness/docs/task-intake.md` (legacy/external section) +- State Hub message to `rein-aharness` + ## Acceptance -- [ ] No Gitea-as-product planning -- [ ] Projection boundary clear -- [ ] Ops claim queue explicitly out of scope for issue-core +- [x] No Gitea-as-product planning +- [x] Projection boundary clear +- [x] Ops claim queue explicitly out of scope for issue-core ## Out of scope -- Implementing ops_run (ACTIVITY-WP-0026) -- Fixing Forgejo PAT for rest sink (only if projection needs it later) -- GitHub/Jira connectors +- Implementing ops_run (ACTIVITY-WP-0026) — lives in activity-core / REINAH-WP-0005 +- Fixing Forgejo PAT for rest sink (only if projection needs it later) — ISSUE-WP-0003 residual / deploy +- GitHub/Jira connectors — product growth, not this workplan + +No new residual: those items already have live work records elsewhere. + +## Close note (2026-09-14) + +Product language is **Forgejo** only. The HTTP API is Gitea-compatible; +module `issue_core.backends.gitea` and backend type `gitea` remain as +identifiers. `FORGEJO_TOKEN` is preferred; `GITEA_*` is a deprecated alias. +INTENT/SCOPE quote ACT-ADR-005: issue-core is not the ops claim queue.