# SCOPE — issue-core Concrete in-scope / out-of-scope decisions for issue-core. Paired with `INTENT.md`, which explains *why*; this file states *what* and *what not*. **Fleet framing (2026-07-20):** issue-core is a **connector** to external issue trackers and the home of the **UUID ↔ external-id mapping** concept. Work records originate as repo files (ADR-001 / work-record canon). See `INTENT.md` and `docs/uuid-external-id-mapping.md`. ## In scope ### External-issue CRUD across backends - **Create** issues with title, description, labels, priority, type, milestone, assignee, due date — when a tracker projection or direct tracker use is intentional. - **Read** individual issues and lists with filters (state, labels, priority, assignee, text search). - **Update** any mutable field on existing issues. - **Close / reopen** with state transitions enforced at the domain layer. - **Delete** where the backend allows (local SQLite; soft-archive elsewhere). - **Comment** threads on issues. ### Backend abstraction - A single `IssueBackend` ABC contract that every backend implements. - `BackendCapabilities` declares which optional features a backend supports (bulk update, search, milestones, etc.). - A `BackendFactory` registry that maps config to backend instances. ### Backends shipped today - **Local SQLite** — offline cache / source of truth for disconnected work. - **Gitea** — REST API integration (Forgejo-compatible in deployment). ### Backends planned - **GitHub** — Issues + PRs. - **GitLab** — Issues. - **JIRA** — issues with story-points. ### UUID ↔ external-id mapping (target shape) Primary *fleet* responsibility of issue-core going forward — design only in this repo until stage-3 work-record architecture lands; see `docs/uuid-external-id-mapping.md`. | Concept | Meaning | | --- | --- | | Internal id | Work-record UUID (UUIDv7, hub-assigned / written back to source file as `state_hub_*_id`-style fields) | | External id | Backend issue id / number + backend identity (`gitea`, `github`, …) | | Mapping row | Durable association: which work record projects to which external issue | | Direction | Create projection outward; optional two-way sync of status/comments at the boundary | Named here so scope and design agree before implementation. ### Operator / automation surfaces - **CLI** (`issue` / `issue-core`) for humans and agents on a shell — direct backend use. - **REST** (`POST /issues/`) for **intentional** external-issue creation by authenticated clients. Remains useful; is **not** the primary fleet path for originating work. - **REST worker surface** (`GET/PATCH /issues/`) for executors that operate on external issues when a tracker is in the loop: list, claim (`in_progress` + assignee), close. - **NATS subscriber** (design stub only — see `docs/nats-task-ingestion.md`). Any NATS path must respect the same rule: external projection only when explicitly configured, never silent origin of fleet work. ### Synchronization - Local SQLite ↔ remote backends, bidirectional. - `get_issues_modified_since()` on `SyncableBackend` for incremental sync. - Conflict resolution via `SyncableBackend.resolve_sync_conflict()`. - Sync metadata (last-synced timestamps, remote IDs) stored on `Issue.sync_metadata`. ### State Hub integration - Registered as a custodian-domain / infotech repo. - May emit `add_progress_event()` on significant *connector* lifecycle moments (e.g. projection created, mapping updated) when wired. - Does **not** replace work-record indexing; state-hub remains the fleet read model for work records. ## Out of scope ### Origin of fleet work Creating workplans, tasks, intake items, decisions, or engagements. Those originate as repo files (or schema-valid blocks in context) and are indexed by `fix-consistency` / state-hub. issue-core may **project** an existing work record to a tracker; it does not birth the work record. ### Default sink for internal automation findings activity-core (and similar) must not treat `POST /issues/` as the always-on default for every matched rule. That product decision lives in the emitter; issue-core will keep accepting authenticated POSTs but will not redefine itself as the landing zone. Follow-up: activity-core IssueSink policy (filed from ISSUE-WP-0004-T05). ### Project management Phases, campaigns, milestones-as-plans, dependency graphs between tasks, gantt-style scheduling, OKR linkage. **That is workplan / project tooling.** issue-core operates on individual external issues. A milestone field exists for flat grouping, not for multi-stage plans. ### Spawn audit trail When an emitter files an issue here, the *spawn event* (who fired, which rule, which triggering event) is recorded by the **emitter** (e.g. activity-core's `task_spawn_log`). issue-core stores the resulting issue and traceability fields (`triggering_event_id` today; work-record UUID in the mapping design) — nothing more. Symmetrically: issue-core does not push fleet work-record status updates back to activity-core. Lifecycle of external issues stays here; lifecycle of work records stays in repo files + hub. ### Event bus / message broker Inter-service communication runs on NATS managed elsewhere. issue-core consumes specific subjects (future) and exposes a REST surface; it does not relay events between other services. ### Notifications Telling a human "your task changed" is the job of the relevant UI, digest, chatbot, or notification service — not issue-core. ### Workflow / approval engine State machine is intentionally small (OPEN, CLOSED, IN_PROGRESS, BLOCKED). Conditional routing, approval chains, multi-step workflows, SLA timers — all out of scope. ### UI issue-core is CLI + REST first. Each backend brings its own native UI (Gitea web, GitHub web, etc.) and that is enough. A web UI for issue-core itself is not on the roadmap. ### Identity / access management Authentication relies on backend credentials (Gitea tokens, GitHub tokens) and on a service-level API key for the REST endpoint. issue-core is not a user directory. ## Integration boundaries ### Fleet primary path (work records) | Step | Owner | Notes | | --- | --- | --- | | Author / promote work record | Repo files + tools | ADR-001 / work-record canon | | Index + UUID write-back | state-hub `fix-consistency` | Hub is read model | | Claim / execute | Humans, agents, harness | On work records, not on Forgejo by default | | Optional external projection | **issue-core** | Mapping table + backend CRUD | ### Surfaces into issue-core | Client | Transport | Payload | Role | | --- | --- | --- | --- | | Human CLI | local process | CLI args | Direct tracker use / admin | | Agents | REST or CLI | issue fields or CLI args | Tracker ops when in the loop | | activity-core IssueSink | REST `POST /issues/` | `TaskSpec` | **Optional, intentional** external issues only — not default for internal findings | | Future mapping API | REST/CLI (TBD) | work_record_uuid + project request | Primary fleet connector path once designed | ### Downstream consumers | Consumer | Mechanism | Notes | | --- | --- | --- | | Humans / external collab | backend UI, `issue list` | Third-party tracker collaboration | | state-hub | work-record index; optional progress events | Fleet orientation | | Backend remote (e.g. Gitea) | direct backend write | Storage / projection layer | ### `TaskSpec` payload (activity-core IssueSink — retained contract) Still accepted for backward compatibility and intentional emits: ```json { "title": "string", "description": "string", "target_repo": "string", "priority": "high | medium | low", "labels": ["string"], "due_in_days": 7, "source_type": "rule | instruction", "source_id": "string", "triggering_event_id": "event uuid or stable source key", "activity_definition_id": "string" } ``` `triggering_event_id` is accepted as a non-empty string. Event-driven emissions should send the upstream activity event UUID. Scheduled or cron emissions that do not have a concrete event row may send a stable source key such as `scheduled`; issue-core stores the value verbatim in ingestion metadata for traceability. **Relation to mapping design:** `triggering_event_id` is emitter-side traceability, not a work-record UUID. The mapping surface will add an explicit work-record identity field (and durable mapping rows). See `docs/uuid-external-id-mapping.md` — extend rather than overload `triggering_event_id`. ### `POST /issues/` response ```json { "issue_id": "string", "issue_url": "string or null", "backend": "gitea | sqlite | github" } ``` The `issue_id` is the external-issue back-reference an intentional emitter may store (e.g. activity-core `task_spawn_log`). It is owned by issue-core's backend; it is not a work-record UUID. ## See also - `INTENT.md` — why issue-core exists and the connector pivot. - `ROADMAP.md` — feature trajectory. - `docs/uuid-external-id-mapping.md` — mapping design (not yet implemented). - `workplans/ISSUE-WP-0004-align-with-work-record-canon.md` — alignment work. - `the-custodian/canon/standards/work-record-types_v0.1.md` — work-record kinds; task kind notes that issue-core issues are external projections only. - activity-core `docs/adr/adr-001-event-bridge-architecture.md` — IssueSink contract (emitter side; policy follow-up separate).