Ship ExternalSecret actcore-forgejo-admin and multi-policy ESO token bootstrap so weekly prune credentials refresh without bootstrap inject. Disposition issue-core path A and MarkiTect ROS as external owners; close workplan finished.
108 lines
3.6 KiB
Markdown
108 lines
3.6 KiB
Markdown
# Task emission boundary (IssueSink)
|
|
|
|
activity-core owns the decision to spawn a task and the audit trail that says
|
|
why it spawned. It does not own downstream task lifecycle state after emission.
|
|
|
|
## Sink matrix (ACTIVITY-WP-0022)
|
|
|
|
| `ISSUE_SINK_TYPE` | Destination | Default? |
|
|
| --- | --- | --- |
|
|
| **`state-hub`** | State Hub progress `activity_task_spawn` | **Yes** (safe for internal findings) |
|
|
| **`null`** | Synthetic `null-*` refs only | Dry-run / tests |
|
|
| **`rest`** | issue-core `POST /issues/` (may → Forgejo) | **Explicit opt-in only** |
|
|
|
|
Code default when unset: **`state-hub`** (`DEFAULT_ISSUE_SINK_TYPE`).
|
|
|
|
Unknown values fall back to `state-hub` and log a warning.
|
|
|
|
Consumer contract (fields, idempotency, Binky completion):
|
|
`docs/task-emission-consumer-contract.md`.
|
|
|
|
## REST endpoint (opt-in)
|
|
|
|
```text
|
|
POST {ISSUE_CORE_URL}/issues/
|
|
```
|
|
|
|
`IssueCoreRestSink` uses `ISSUE_CORE_API_KEY` via `Authorization: Bearer <key>`:
|
|
|
|
```json
|
|
{
|
|
"title": "Run SBOM rescan for activity-core",
|
|
"description": "",
|
|
"target_repo": "activity-core",
|
|
"priority": "medium",
|
|
"labels": ["sbom", "security", "automated"],
|
|
"due_in_days": null,
|
|
"source_type": "rule",
|
|
"source_id": "flag-stale-sbom",
|
|
"triggering_event_id": "event-or-schedule-key",
|
|
"activity_definition_id": "activity-definition-uuid"
|
|
}
|
|
```
|
|
|
|
Response: `issue_id` (required), optional `issue_url`, `backend`. Stored in
|
|
`task_spawn_log` only as a reference.
|
|
|
|
## State Hub sink (default)
|
|
|
|
Each TaskSpec becomes a progress event (`activity_task_spawn` by default).
|
|
No Forgejo issue is created. Suitable for Binky rhythm, internal hygiene, and
|
|
any definition that should not spam trackers.
|
|
|
|
## REST versus NATS
|
|
|
|
Keep REST as the issue-core transport until issue-core owns a durable NATS
|
|
command consumer. NATS remains appropriate for **event intake** into
|
|
activity-core.
|
|
|
|
## Operating modes
|
|
|
|
- **`state-hub` (default):** fleet-visible spawns without Forgejo.
|
|
- **`null`:** dry-run / audit.
|
|
- **`rest`:** live issue-core; requires healthy backend and intentional policy.
|
|
Railiance production uses `state-hub` unless an overlay explicitly sets
|
|
`rest` for an experiment.
|
|
|
|
### Known production failure (2026-07-21, reconfirmed 2026-07-21 T06) — rest path
|
|
|
|
`POST /issues/` returns **HTTP 503**:
|
|
|
|
```text
|
|
Failed to connect to backend 'forgejo-inbox': Failed to connect to Gitea API
|
|
```
|
|
|
|
**Disposition (ACTIVITY-WP-0023-T06):** activity-core keeps global default
|
|
`state-hub` and does **not** flip production to `rest`. Path A is owned by
|
|
**issue-core**: rotate/fix `GITEA_BACKEND_TOKEN` (Forgejo backend PAT for the
|
|
forgejo-inbox connector — not the activity-core `ISSUE_CORE_API_KEY`
|
|
ingestion key). After issue-core proves `POST /issues/` → **201**, operators may
|
|
opt in per definition / overlay only (WP-0022).
|
|
|
|
Smoke from worker (does not change sink env):
|
|
|
|
```bash
|
|
kubectl -n activity-core exec deploy/actcore-worker -- python -c '
|
|
# valid IssueCore body → expect 201 when path A is healthy, else 503 forgejo-inbox
|
|
'
|
|
```
|
|
|
|
## Promotion to rest (one definition at a time)
|
|
|
|
1. Dry-run with `null` or observe `state-hub` spawns.
|
|
2. Confirm issue-core smoke returns **201**.
|
|
3. Temporarily set `ISSUE_SINK_TYPE=rest` only if policy allows external issues.
|
|
4. Prefer per-definition future opt-in over global rest (WP-0022).
|
|
5. Roll back: `ISSUE_SINK_TYPE=state-hub` or `null` + worker restart.
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
uv run pytest tests/test_issue_sink.py tests/test_integration_event_bridge.py -q
|
|
```
|
|
|
|
## Side-effect resolvers (not IssueSink)
|
|
|
|
`forgejo_package_prune` with `apply: true` is a **declared platform side-effect**,
|
|
not task emission. It requires a non-empty `live_images_file` (ACTIVITY-WP-0023-T03).
|
|
See runbook § Weekly maintenance.
|