# Task emission consumer contract **Audience:** agent-harness, per-repo Temporal workers, operators. **Owners:** activity-core (producer), consumers (executors). **Related:** ACTIVITY-WP-0022 (sink policy), ACTIVITY-WP-0023-T02 (executor gap). activity-core answers **when / what / where**. It does **not** execute work. Consumers must pick up emitted tasks and produce domain evidence. ## Sink matrix (ACTIVITY-WP-0022) | `ISSUE_SINK_TYPE` | Behaviour | When to use | | --- | --- | --- | | **`state-hub` (default)** | POST State Hub `/progress/` with `event_type=activity_task_spawn` (override via `STATE_HUB_TASK_EVENT_TYPE`) | Internal fleet findings; no Forgejo issues | | **`null`** | Synthetic `null-*` refs in `task_spawn_log` only | Dry-run / contract review | | **`rest`** | POST issue-core `/issues/` (may project to Forgejo) | **Explicit opt-in** only when external tracker issues are intended and backend is healthy | Unset or unknown values fall back to **`state-hub`** (safe default). ## Payload: `activity_task_spawn` (State Hub) Produced by `StateHubProgressSink`. Consumers should treat `detail` as the authoritative task spec. ```json { "event_type": "activity_task_spawn", "author": "activity-core", "summary": "", "detail": { "task_ref": "sh-", "title": "Run Binky daily rhythm (daily_brief) for 2026-07-21", "description": "...", "target_repo": "binky-control", "priority": "medium", "labels": ["binky", "rhythm", "automated"], "source_type": "rule", "source_id": "emit-daily-rhythm-task", "triggering_event_id": "manual-… or scheduled", "activity_definition_id": "", "backend": "state-hub-progress" } } ``` ### Required consumer behaviour 1. **Idempotency:** key on `detail.task_ref` or `(activity_definition_id, triggering_event_id, source_id, title)`. 2. **Routing:** use `target_repo` (and labels) to select checkout / lane. 3. **Completion evidence:** post a domain progress event when work finishes (e.g. Binky: `event_type=binky_daily_brief` with `detail.repo=binky-control` and date), so rhythm resolvers can set `due=false`. 4. **Do not** re-implement task lifecycle in activity-core. ## Payload: issue-core REST (`ISSUE_SINK_TYPE=rest`) See `docs/issue-core-emission-boundary.md`. `task_spawn_log.task_ref` is the issue-core `issue_id`. Prefer this only for intentional external projection. ## Binky daily brief path (reference) 1. Schedule / one-shot: `Binky Daily Operating Rhythm`. 2. Context: `binky_rhythm_status` → `due=true` for `daily_brief`. 3. Rule emit → sink (`state-hub` recommended). 4. **Consumer (out of repo):** agent-harness or operator session runs `binky-control` OperatingRhythm / brief scripts. 5. Completion: post `binky_daily_brief` progress so the next fire is not due. Until a harness consumer is wired, operators may complete the brief manually and still post the progress event — spawn without completion leaves `due=true`. ## Anti-patterns - Using `TaskExecutorWorkflow` in activity-core for real work (disabled by default; ACTIVITY-WP-0023-T08). - Global `ISSUE_SINK_TYPE=rest` for all definitions (reintroduces Forgejo spam). - Treating `task_spawn_log` as task status authority.