Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
4.9 KiB
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),
ACT-ADR-005 / ACTIVITY-WP-0026 (ops_run claim queue).
activity-core answers when / what / where. It does not execute work. Consumers must pick up emitted tasks and produce domain evidence.
Primary claim path: ops_run (ACTIVITY-WP-0026)
For scheduled / automation work (FI daily brief, Binky rhythm, etc.), the
authoritative claimable instance is an ops_run row in activity-core — not
issue-core and not a Forgejo ticket.
| Step | Call | Notes |
|---|---|---|
| List / poll | GET /ops-runs?state=open |
Filter by labels via claim body |
| Claim | POST /ops-runs/claim |
{ worker_id, labels?, limit?, lease_seconds? } |
| Heartbeat | POST /ops-runs/{id}/heartbeat |
Extend lease during long runs |
| Complete | POST /ops-runs/{id}/complete |
{ worker_id, result } |
| Fail | POST /ops-runs/{id}/fail |
{ worker_id, error, reopen? } |
The worker token is bound by Activity Core to one configured worker_id.
Every worker mutation must name that exact identity and must still hold a lease
whose deadline is strictly in the future. Operator/SSO authentication does not
substitute for worker authentication on these calls.
Full field list, auth, and env: docs/ops-run-queue.md.
Consumer implementation (rein-aharness): REIN-A-0002.
Emit dual-writes: ops_run (claim) + existing IssueSink progress (state-hub
by default) + task_spawn_log audit. Do not treat activity_task_spawn
progress as a claim queue — it is visibility only.
Sink matrix (ACTIVITY-WP-0022) — dual-write / projection
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).
Ops claim does not require rest or issue-core.
Payload: activity_task_spawn (State Hub)
Produced by StateHubProgressSink. Consumers should treat detail as the
visibility task spec; claim authority is ops_run when the queue is enabled.
{
"event_type": "activity_task_spawn",
"author": "activity-core",
"summary": "<task title, max ~240 chars>",
"detail": {
"task_ref": "sh-<uuid>",
"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": "<activity uuid>",
"backend": "state-hub-progress"
}
}
Required consumer behaviour
- Idempotency: key on
detail.task_refor(activity_definition_id, triggering_event_id, source_id, title). - Routing: use
target_repo(and labels) to select checkout / lane. - Completion evidence: post a domain progress event when work finishes
(e.g. Binky:
event_type=binky_daily_briefwithdetail.repo=binky-controland date), so rhythm resolvers can setdue=false. - 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)
- Schedule / one-shot:
Binky Daily Operating Rhythm. - Context:
binky_rhythm_status→due=truefordaily_brief. - Rule emit → sink (
state-hubrecommended). - Consumer (out of repo): agent-harness or operator session runs
binky-controlOperatingRhythm / brief scripts. - Completion: post
binky_daily_briefprogress 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
- Adding an in-repo task executor or reviving the retired
TaskExecutorWorkflow/task_instancessurface. - Global
ISSUE_SINK_TYPE=restfor all definitions (reintroduces Forgejo spam). - Treating
task_spawn_logor State Hubactivity_task_spawnas claim authority (usePOST /ops-runs/claim— ACT-ADR-005). - Using issue-core or Forgejo as the ops automation claim queue.
- Product references to Gitea — self-hosted forge is Forgejo only.