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
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:
- See whether regular scheduled jobs ran properly in a time window
- Review results (runs → evidence: tasks spawned, progress refs, report paths)
- Run a defined activity instantly
- 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-forwardto 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"
- Write a short product brief (personas, jobs-to-be-done, non-goals) in this
workplan or
docs/if it outgrows the file. - Update
SCOPE.md: operator automation console is in scope; end-user task management remains out of scope. Clarify G10 (auth MVP vs public access). - 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"
- Design operator token check (e.g.
X-Operator-Tokenheader compared to env/secret) for all/opsmutations. - Fail-closed: when production requires auth and token is missing/misconfigured, mutations return 401/403 (not silent open).
- Document env name, ESO/bootstrap option, and port-forward access in runbook.
- 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"
GET /ops/automations— inventory equivalent to WP-0019 (cron/scheduled defs, enabled, schedule expression, Temporal schedule id / paused hints).GET /ops/automations/status?since=&until=— wraps WP-0018 automation-status semantics (JSON).GET /ops/automations/{id}— single definition + schedule visibility.- 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"
GET /ops/automations/{id}/runs?since=(and optional global runs list).- Each run:
run_id,scheduled_for,fired_at,tasks_spawned, version, and evidence pointers when known (progress event types/ids, report paths). - 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"
POST /ops/automations/{id}/trigger— wrap existing trigger path; require operator token.POST .../enable|disable|pause|unpause— one coherent model documented in the PR (definition flag vs Temporal pause; prefer both consistent).- Audit trail: who/when/what (operator label optional; never store token). Prefer
State Hub progress
event_typeor a smallops_audittable. - 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"
- Contract tests for inventory/status/runs JSON shapes.
- Auth tests: missing token → reject mutations; valid token → allow.
- 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"
- Static/HTMX (or minimal SPA) served from actcore-api under
/ops/ui/. - Screens: automation list, status window, run history, Run now + enable/pause actions (token supplied via header or session form — no token in URLs).
- Schedule fields read-only in MVP.
- 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"
- Document port-forward (or equivalent) for railiance01.
- Wire operator token into runtime secret path (bootstrap or ESO) without committing values.
- Explicitly do not add public Ingress in this workplan.
- 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"
- Map “How did automations go since Sunday?” to
/ops/automations/statusand UI. - Map “Run X now” and “pause X” to mutation endpoints.
- 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_runsevidence - 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
- T01 SCOPE/brief
- T02 auth + T03/T04/T05 API (before UI)
- T06 tests green
- T07 thin UI
- T08/T09 docs + prod token wiring
- 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.pydocs/runbook.md,SCOPE.mdG10- 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.