--- id: ISSC-WP-0001 type: workplan domain: custodian repo: issue-core status: done state_hub_workstream_id: 1135fc1d-1f46-4e35-886d-04cc3b8050b6 tasks: - id: T01 title: Rename package issue-facade → issue-core throughout state_hub_task_id: b7054428-82a9-4d81-bfa8-5b5ee2eaf69f status: done - id: T02 title: Register issue-core in state hub under capabilities domain state_hub_task_id: b1d36996-44ff-48b9-b208-709d6874453c status: done - id: T03 title: Write INTENT.md state_hub_task_id: 265c6338-0310-409d-a081-6446042f6274 status: done - id: T04 title: Update or write SCOPE.md state_hub_task_id: f95ac730-7ba0-4eae-bcae-de1e7d24b164 status: done - id: T05 title: Implement task ingestion REST endpoint POST /issues/ state_hub_task_id: 26af07e4-c072-42ad-bb5c-facb196156c9 status: done - id: T06 title: Document NATS subscriber interface (design stub) state_hub_task_id: dff61fed-1e8c-4eb3-bbd6-1e3742329945 status: done created: "2026-05-14" --- # ISSC-WP-0001: Rename to issue-core and Task Ingestion ## Purpose Two things need to happen to make issue-facade a proper peer to activity-core in the Coulomb org architecture: 1. **Rename**: `issue-facade` is a misleading name — it is not a facade, it is the authoritative task lifecycle manager for the org. Renaming to `issue-core` aligns with the naming pattern (`activity-core`, `rules-core`, `project-core`) and signals its role clearly. 2. **Task ingestion endpoint**: activity-core's `IssueSink` adapter emits tasks to issue-core via REST. That endpoint must exist, be stable, and accept `TaskSpec` payloads from activity-core. Without it, activity-core's task emission is a no-op. ## Context - **activity-core WP-0003** (in progress): implements `IssueSinkAdapter` in `src/activity_core/issue_sink.py`. It calls `POST /issues/` on issue-core to create tasks from ActivityDefinition rule/instruction output. - **issue-facade** currently: multi-backend task tracker (Gitea, SQLite, GitHub). Handles task creation and tracking. Has no incoming task-ingestion API from external callers like activity-core. - **State hub gap**: issue-facade is not registered in the Custodian State Hub. This makes cross-repo workstream tracking impossible. See: `docs/adr/adr-001-event-bridge-architecture.md` in activity-core for the IssueSink adapter design and the task emission flow. ## Scope **In scope:** - Package rename (issue-facade → issue-core) - State hub registration - INTENT.md and SCOPE.md - Task ingestion REST endpoint (POST /issues/) - NATS subscriber interface design stub **Out of scope:** - Project management features (that is project-core, future) - UI or end-user facing changes - Changing backends (Gitea/SQLite/GitHub adapters stay as-is) ## TaskSpec Payload (from activity-core) The POST /issues/ endpoint receives this payload from activity-core's IssueSink: ```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": "uuid", "activity_definition_id": "string" } ``` The endpoint must return: ```json { "issue_id": "string", "issue_url": "string or null", "backend": "gitea | sqlite | github" } ``` The `issue_id` is stored in activity-core's `task_spawn_log` as the external reference. It is not managed by activity-core — it belongs to issue-core. ## Tasks ### T01 — Rename package issue-facade → issue-core throughout Rename everywhere: - `pyproject.toml`: `name = "issue-core"`, update entry points - Python package directory: `issue_facade/` → `issue_core/` (if applicable) - All `import issue_facade` → `import issue_core` - CLI command names if changed - README, CHANGELOG headers - Docker Compose service names and image tags - Any Makefile targets After renaming, run the full test suite to confirm no broken imports. Update the `workplans/` frontmatter `repo: issue-core` once renamed. ### T02 — Register issue-core in state hub under capabilities domain Call `register_repo()` on the state hub MCP: ``` register_repo(slug="issue-core", path="/home/worsch/issue-facade", domain="custodian") # or capabilities if domain exists ``` Note: the directory may still be named `issue-facade` — register with `slug="issue-core"` and update `path` once the rename is complete. ### T03 — Write INTENT.md Write `INTENT.md` explaining: - **Why it exists**: the Coulomb org needs a single, observable place where tasks land — regardless of whether they were created by a human, by activity-core, or by an agent. - **What it is**: task lifecycle manager (create, assign, update, close) with pluggable backends (Gitea, SQLite, GitHub). - **What it is NOT**: not a project manager (no phases, no campaigns, no dependency graphs — that is project-core). Not a spawn audit trail (that is activity-core). Not an event bus. - **How it fits**: activity-core emits tasks here via IssueSink; humans and agents consume them; status updates flow back to issue-core (not to activity-core). ### T04 — Update or write SCOPE.md Check if SCOPE.md exists. If yes, update: - Rename references (issue-facade → issue-core) - Add activity-core as an upstream emitter via IssueSink REST - Add "Out of scope" section if missing: project management, spawn audit trail - Update "How it fits" to reference activity-core architecture If no SCOPE.md exists, write one from scratch following the standard format. ### T05 — Implement task ingestion REST endpoint POST /issues/ Implement `POST /issues/` (or `POST /tasks/create` — check existing naming convention and pick the most consistent path): ```python @router.post("/issues/") async def ingest_task(payload: TaskIngestionRequest) -> TaskIngestionResponse: ... ``` The endpoint must: 1. Validate the payload against `TaskIngestionRequest` schema 2. Route to the correct backend based on `target_repo` (look up backend config) 3. Create the issue/task in the backend 4. Return `{issue_id, issue_url, backend}` 5. Log the ingestion with `triggering_event_id` for traceability **Key file**: `issue_core/api/ingest.py` (new) or alongside existing API routes. Security: the endpoint should require an API key header (check existing auth pattern in the codebase). Do not expose it unauthenticated. ### T06 — Document NATS subscriber interface (design stub) **Design stub only — implementation deferred until activity-core's IssueSink migrates from REST to NATS.** Document in `docs/nats-task-ingestion.md`: - Proposed NATS subject pattern: `act.tasks.create.{target_repo}` - Message schema (same `TaskIngestionRequest` as REST endpoint) - Consumer group config: durable consumer, at-least-once delivery - Idempotency key: `triggering_event_id` — used to deduplicate retries ## Build Order ``` T01 (rename) → T02 (register) → T03 (INTENT.md) → T04 (SCOPE.md) T01 (rename) → T05 (REST endpoint) → T06 (NATS stub, depends on endpoint schema) ``` ## Completion Criteria 1. Package renamed to issue-core; all tests pass after rename 2. issue-core registered in state hub 3. INTENT.md and SCOPE.md committed and accurate 4. `POST /issues/` endpoint implemented and tested with a TaskSpec payload 5. activity-core agent confirmed IssueSink integration works end-to-end 6. NATS design stub committed to `docs/` ## Notes - **Directory name**: if renaming the repository directory (`issue-facade/` → `issue-core/`) — coordinate with Bernd first, as it will invalidate any relative paths or symlinks. - **T01 first**: all other tasks depend on the rename being settled. The directory path in state hub registration (T02) must reflect the final name. - **Backwards compat**: if external consumers call the existing API using paths under `issue-facade`, add redirect aliases rather than breaking them. - The NATS stub (T06) should be implemented as a comment-heavy skeleton so the activity-core agent can wire the NATS IssueSink without waiting for a full implementation. ## Change History - v0.1 (2026-05-14): Stub created by activity-core agent during WP-0003 planning. Local agent to flesh out and implement.