activity-core/workplans/ACTIVITY-WP-0024-operator-automation-console.md
codex 1fa9dc18ab
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
fix(workplans): adopt ADR-007 derived identifiers for unregistered records
These workplans exist only in the retired local hub. Their random pre-ADR-007
identifiers are refused by C-06 as stale references, so they cannot be
registered. Deriving from the canonical record id takes no identity from
anything: central does not hold them and the old ids die with the cache.

Records central already holds were deliberately left untouched.

Refs CUST-WP-0068-T06

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-25 20:05:21 +02:00

10 KiB

id type title domain repo status owner topic_slug created updated state_hub_workstream_id
ACTIVITY-WP-0024 workplan Operator automation console (status API + control plane + thin UI) infotech activity-core finished codex activity-core 2026-07-21 2026-07-21 555c6bb2-73e2-579b-837b-a7df0daad621

Operator automation console (status API + control plane + thin UI)

Origin

2026-07-21 operator discussion after ACTIVITY-WP-0023 closeout. Operators need a first-class surface for scheduled automations without SSH + SQL + Temporal UI as the only path.

Builds on:

  • ACTIVITY-WP-0018 — own-infra automation status contract + CLI
  • ACTIVITY-WP-0019 — automation inventory Make targets
  • ACTIVITY-WP-0021 — prod automation status path (SSH helper)

Locked product decisions (founder):

Fork Choice
Surface API-first, then thin UI in activity-core
Auth / exposure Port-forward + operator token; mutations fail-closed
Mutations in MVP Run now + enable/disable/pause only (no cron patch)

Goal

Ship an operator automation console: HTTP APIs (and a thin UI) so an operator can:

  1. See whether regular scheduled jobs ran properly in a time window
  2. Review results (runs → evidence: tasks spawned, progress refs, report paths)
  3. Run a defined activity instantly
  4. Enable / disable / pause schedules without SSH + SQL

This is not an end-user task tracker and not a second scheduler. Durable scheduling remains Temporal + ActivityDefinitions; the console is a consumer and control plane over that truth.

Out of scope

  • Cron / timezone patching in the UI (schedules stay git-owned; edit files + sync)
  • Authoring or editing rules / instructions / templates in the UI
  • Public Ingress or full OIDC/key-cape (follow-up after MVP)
  • Hosting the console in the state-hub dashboard
  • Task lifecycle UI (create/assign/close tasks)
  • Replacing Temporal UI for workflow debugging (deep-link only)

Architecture sketch

Operator (browser / curl)
    │  port-forward svc/actcore-api
    │  header: X-Operator-Token (or agreed scheme)
    ▼
actcore-api
  GET  /ops/automations
  GET  /ops/automations/status?since=&until=
  GET  /ops/automations/{id}
  GET  /ops/automations/{id}/runs
  POST /ops/automations/{id}/trigger
  POST /ops/automations/{id}/enable|disable|pause|unpause
  GET  /ops/ui/…  (static/HTMX after API stable)
    │
    ├─ activity_core.automation_status + inventory
    ├─ activity_runs + activity_definitions
    ├─ Temporal schedule describe / pause
    └─ audit (progress event or ops_audit; no secrets)

Deep-links into State Hub / edge when evidence lives there (G9-aware).

Auth policy (MVP)

  • Mutations require an operator token when ACTIVITY_CORE_OPERATOR_TOKEN (or ESO-backed secret) is configured.
  • If the token is unset under production posture, refuse mutations (fail-closed). Do not leave unauthenticated Run now on a reachable API.
  • Token never logged or returned by API.
  • Access path: kubectl -n activity-core port-forward to the API Service.
  • Ingress + OIDC is an explicit follow-up, not an MVP gate.
  • Credential custody via warden route / OpenBao — never git, chat, or workplan.

Mutation policy (MVP)

Action Behavior
Run now Wrap existing trigger; return workflow_id; UI confirms for side-effect defs
Disable / pause Coherent model: definition enabled flag and/or Temporal schedule paused
Enable / unpause Inverse
Change cron / timezone Out of MVP — definition files + sync; UI shows schedule read-only

Tasks

Task: Product brief and SCOPE clarification

id: ACTIVITY-WP-0024-T01
status: done
priority: high
state_hub_task_id: "697113b1-793e-59b4-a86c-ecb22ad127b6"
  1. Write a short product brief (personas, jobs-to-be-done, non-goals) in this workplan or docs/ if it outgrows the file.
  2. Update SCOPE.md: operator automation console is in scope; end-user task management remains out of scope. Clarify G10 (auth MVP vs public access).
  3. Cross-link WP-0018/0019 as the evidence/inventory contracts this console wraps.

Done when: SCOPE and this workplan tell one story; non-goals are explicit.

Task: Operator token auth and fail-closed mutations

id: ACTIVITY-WP-0024-T02
status: done
priority: high
state_hub_task_id: "0e709610-2599-593c-9341-1ca5b6ddd23e"
  1. Design operator token check (e.g. X-Operator-Token header compared to env/secret) for all /ops mutations.
  2. Fail-closed: when production requires auth and token is missing/misconfigured, mutations return 401/403 (not silent open).
  3. Document env name, ESO/bootstrap option, and port-forward access in runbook.
  4. Never put the token value in git, logs, or State Hub detail.

Done when: unauthenticated mutation is rejected under the configured posture; runbook describes custody without leaking secrets.

Task: REST inventory and status window

id: ACTIVITY-WP-0024-T03
status: done
priority: high
state_hub_task_id: "8adf73b7-d23e-5110-8dc4-a22fc0bde448"
  1. GET /ops/automations — inventory equivalent to WP-0019 (cron/scheduled defs, enabled, schedule expression, Temporal schedule id / paused hints).
  2. GET /ops/automations/status?since=&until= — wraps WP-0018 automation-status semantics (JSON).
  3. GET /ops/automations/{id} — single definition + schedule visibility.
  4. Prefer reusing activity_core.automation_status / inventory modules; do not fork a second evidence contract.

Done when: curl against the API matches CLI status/inventory for the same window within documented field mapping.

Task: REST run history and evidence summary

id: ACTIVITY-WP-0024-T04
status: done
priority: high
state_hub_task_id: "26d50f76-67cc-5719-a94a-b2de9c1a1495"
  1. GET /ops/automations/{id}/runs?since= (and optional global runs list).
  2. Each run: run_id, scheduled_for, fired_at, tasks_spawned, version, and evidence pointers when known (progress event types/ids, report paths).
  3. No secrets, raw LLM output, or full prompts in responses.

Done when: operator can list recent runs for a definition without SSH/SQL.

Task: REST Run now and enable/disable/pause with audit

id: ACTIVITY-WP-0024-T05
status: done
priority: high
state_hub_task_id: "aff1ddc5-4e78-5f6f-98da-2b2e0af5f2f9"
  1. POST /ops/automations/{id}/trigger — wrap existing trigger path; require operator token.
  2. POST .../enable|disable|pause|unpause — one coherent model documented in the PR (definition flag vs Temporal pause; prefer both consistent).
  3. Audit trail: who/when/what (operator label optional; never store token). Prefer State Hub progress event_type or a small ops_audit table.
  4. Side-effect definitions (e.g. forgejo prune apply): document confirmation expectation for UI; API may accept an explicit confirm_side_effect=true.

Done when: authenticated Run now returns workflow_id and produces run evidence; pause/disable visible in inventory/status; unauthenticated mutate fails.

Task: Contract and security tests

id: ACTIVITY-WP-0024-T06
status: done
priority: high
state_hub_task_id: "0c07917b-b195-5cbe-a739-bd3a1ae752bb"
  1. Contract tests for inventory/status/runs JSON shapes.
  2. Auth tests: missing token → reject mutations; valid token → allow.
  3. Response fixtures scanned so secrets/token values never appear.

Done when: CI/unit tests cover the above without a live cluster.

Task: Thin operator UI on actcore-api

id: ACTIVITY-WP-0024-T07
status: done
priority: medium
state_hub_task_id: "d2002e29-a1c5-54ff-b2a8-e63db7fe22bb"
  1. Static/HTMX (or minimal SPA) served from actcore-api under /ops/ui/.
  2. Screens: automation list, status window, run history, Run now + enable/pause actions (token supplied via header or session form — no token in URLs).
  3. Schedule fields read-only in MVP.
  4. Confirm dialog for side-effect activities.

Done when: usable via port-forward for the MVP jobs-to-be-done.

Task: Prod access path (no public Ingress)

id: ACTIVITY-WP-0024-T08
status: done
priority: medium
state_hub_task_id: "3b595a46-38e0-51e1-bfee-814de684f8f5"
  1. Document port-forward (or equivalent) for railiance01.
  2. Wire operator token into runtime secret path (bootstrap or ESO) without committing values.
  3. Explicitly do not add public Ingress in this workplan.
  4. Optional: NetworkPolicy note that API remains ClusterIP.

Done when: runbook has a copy-paste operator access path; G10 public access still deferred with a clear follow-up note.

Task: Runbook daily checklist mapping

id: ACTIVITY-WP-0024-T09
status: done
priority: low
state_hub_task_id: "36a3cac3-388b-5f58-94ca-814e494d08f3"
  1. Map “How did automations go since Sunday?” to /ops/automations/status and UI.
  2. Map “Run X now” and “pause X” to mutation endpoints.
  3. Keep CLI (make automation-status) as valid offline/alternate path.

Done when: runbook section points operators at console first, CLI second.

Success criteria

  • GET /ops/automations/status?since=… matches CLI automation-status semantics
  • Operator can list last N runs for a definition without SSH/SQL
  • Authenticated Run now returns workflow_id and produces activity_runs evidence
  • Pause/disable visible in inventory/status and Temporal schedule state
  • Unauthenticated mutation returns 401/403 when token is configured
  • Thin UI usable via port-forward for the above flows
  • Runbook documents token custody without secrets in git

Implementation order

  1. T01 SCOPE/brief
  2. T02 auth + T03/T04/T05 API (before UI)
  3. T06 tests green
  4. T07 thin UI
  5. T08/T09 docs + prod token wiring
  6. Deploy railiance01 image; no Ingress

References

  • Plan: session plan “Operator dashboard for activity-core scheduled automations”
  • src/activity_core/automation_status.py, src/activity_core/api.py
  • docs/runbook.md, SCOPE.md G10
  • ACTIVITY-WP-0018, ACTIVITY-WP-0019, ACTIVITY-WP-0021

Closeout 2026-07-21

Implemented /ops console: auth (fail-closed operator token), inventory/status/runs REST, Run now + enable/disable/pause with audit buffer + optional State Hub progress, thin HTML UI at /ops/ui, runbook + k8s access docs. Tests: tests/test_ops_auth.py, tests/test_ops_console_api.py.