Files
Gitea-Tools/docs/webui-local-dev.md
T
jcwalker3andClaude Opus 4.8 5494696227 feat(webui): read-only system-health API (Closes #634)
Adds `GET /api/v1/system/health`, a structured read-only health surface for
automated readiness checks, and keeps `/health` as the cheap liveness probe.

webui/system_health.py composes a DTO from fail-soft dependency probes: the
control-plane database, the local checkout, and — opt-in via `?deep=1` — live
Gitea reachability, each carrying status, reason, and probe latency. Required
probes drive readiness; the optional Gitea probe can only degrade overall
status, because local inventory stays serveable when the remote is
unreachable. A probe that did not run leaves readiness incomplete rather than
silently passing.

Read-only throughout: the control-plane database is opened through a `mode=ro`
URI because `ControlPlaneDB.__init__` creates directories and runs migrations,
which a health check must never do. No restart or reload control is exposed;
those are Phase 2 and #630 forbids process-kill recovery.

No unproven claims: `stale_runtime.mutation_safe` is true only when the
runtime, checkout, and remote commits are all known and equal, and MCP
namespaces always report `unproven` because a web process cannot exercise the
IDE-managed client path (#543). Probe details are redacted at the browser
boundary — URLs lose userinfo and query strings, credential-shaped text is
masked.

`/health` is expanded additively: every MVP key is retained, plus `started_at`,
`uptime_seconds`, and a pointer to the versioned API. The versioned route
returns 503 when not ready so automation can branch on the status code alone.

Verified at master 9eb0f29: focused file 40 passed / 11 subtests; `-k "webui or
health"` 230 passed / 159 subtests; full suite 4358 passed with the 11
pre-existing master-drift failures unchanged from the clean-master baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-22 15:59:30 -05:00

13 KiB

Internal web UI — local development (#426)

Read-only MVP skeleton for the MCP Control Plane operator console. Gitea, MCP capability gates, and skills/llm-project-workflow/ remain the source of truth; this UI only provides route stubs and layout.

Prerequisites

  • Python 3.11+ with project dependencies installed (pip install -r requirements.txt)
  • No secrets in repo, config, or client bundle

Start the server

From the repository root (or an issue worktree):

./scripts/run-webui

Or directly:

python3 -m webui

Optional environment variables:

Variable Default Purpose
WEBUI_HOST 127.0.0.1 Bind address (keep local for MVP)
WEBUI_PORT 8765 Listen port
WEBUI_REPO_ROOT repository root Prompt library workflow hash root
WEBUI_PROJECT_REGISTRY packaged JSON Project registry path
GITEA_MCP_CONFIG unset Server-side MCP profile config (never sent to browser)
GITEA_MCP_PROFILE unset Active MCP profile name (server-side only)

See webui-deployment.md for internal-only serving, Cloudflare Access/WARP/VPN guidance, and unsafe bind overrides (#435).

See architecture/webui-control-plane-console-architecture-adr.md for the console architecture: layer and authority boundaries, the redaction boundary, /api/v1/... versioning, the target page map, and the phase gates that govern when a write path may open (#632, epic #631).

Routes (MVP)

Path Description
/ Home / operator overview
/health JSON liveness (status, service, mode, timestamp, uptime_seconds)
/api/v1/system/health Structured read-only system health (#634)
/queue Live PR and issue queue dashboard (#429)
/api/queue JSON queue export with pagination metadata
/projects Project registry list (#427)
/projects/{id} Project detail + onboarding checklist
/api/projects JSON registry export
/prompts Prompt library with per-prompt copy buttons (#428)
/api/prompts JSON prompt export with workflow hashes
/runtime MCP runtime health and stale detection (#430)
/api/runtime JSON runtime health export
/audit Report audit paste + validator preview (#431)
/api/audit JSON validator preview (POST report_text, optional task_kind)
/worktrees Worktree hygiene dashboard (#432)
/api/worktrees JSON worktree scan with classifications and anomalies
/actions Gated write-action registry — all disabled in MVP (#434)
/api/actions JSON action registry with capability metadata
/api/actions/{id}/preview Mutation ledger preview (GET, read-only)
/leases Lease and collision visibility (#433)
/api/leases JSON lease/collision export

Most routes are GET-only. POST/PUT/PATCH/DELETE return 405 with read-only-mvp, except /audit and /api/audit which accept POST for local validator preview only (no Gitea mutations, no server-side storage).

System health API (#634)

GET /api/v1/system/health is the structured, read-only health surface for automated readiness checks. It is the first console API under the /api/v1 prefix; the unversioned MVP exports remain as compatibility aliases.

/health is unchanged for existing consumers — every MVP key is still present — and now also carries started_at, uptime_seconds, and a system_health_api pointer. It stays deliberately cheap and runs no dependency probe, because answering readiness costs real work.

Status codes. 200 when ready, 503 when a required dependency failed or was never probed. Automation can branch on the code without parsing the body.

Query flags. The Gitea check is a network call, so it is opt-in: GET /api/v1/system/health?deep=1 runs it and caches the result for WEBUI_HEALTH_PROBE_TTL_SECONDS (default 15s) so dashboard polling does not amplify into remote load. Without the flag that probe reports skipped.

Dependencies. control_plane_db and repository are required and drive readiness. gitea is optional: when it fails the overall status degrades but readiness.ready stays true, because local inventory is still serveable. Each entry carries status, detail, required, and latency_ms.

Two honesty rules are worth knowing before reading the payload:

  • stale_runtime.mutation_safe is true only when the runtime, checkout, and remote-tracking commits are all known and equal. An unfetched remote is reported as indeterminate, never as safe.
  • mcp_namespaces entries are always unproven. A web process runs outside the IDE-managed MCP client and cannot invoke a namespace tool, so per #543 only a client_namespace probe can prove that path.

Sample response (abridged, healthy):

{
  "status": "ok",
  "service": "mcp-control-plane-webui",
  "mode": "read-only",
  "api": "/api/v1/system/health",
  "timestamp": "2026-07-22T11:04:18.512034+00:00",
  "readiness": { "ready": true, "complete": true, "reasons": [] },
  "version": {
    "git_sha": "620ed6e9a9550b8da2ceb82d9ab8744e8920490f",
    "git_describe": "v1.1.0-898-g620ed6e",
    "control_plane_schema_version": 4,
    "python_version": "3.14.5",
    "known": true
  },
  "process": { "started_at": "2026-07-22T10:58:02.114+00:00", "uptime_seconds": 376.4 },
  "deep_probes_requested": false,
  "dependencies": [
    {
      "name": "control_plane_db",
      "kind": "sqlite",
      "status": "ok",
      "detail": "schema v4 readable",
      "required": true,
      "healthy": true,
      "latency_ms": 1.482,
      "metadata": { "schema_version": 4, "active_leases": 3 }
    },
    { "name": "repository", "kind": "git", "status": "ok", "required": true, "healthy": true },
    { "name": "gitea", "kind": "http", "status": "skipped", "required": false, "healthy": false }
  ],
  "mcp_namespaces": [
    { "namespace": "gitea-author", "required_tool": "gitea_whoami", "status": "unproven" }
  ],
  "stale_runtime": { "stale": false, "determinable": true, "mutation_safe": true, "reasons": [] },
  "probe_errors": []
}

No restart, reload, or process-kill control is exposed here: those are Phase 2 at the earliest, and #630 forbids process-kill recovery outright. Every probe opens its subject read-only — the control-plane database is opened through a mode=ro URI so a health check can never create or migrate a schema.

Report audit (#431)

Paste an LLM final report at /audit or POST JSON to /api/audit. The UI reuses final_report_validator and review schema checks to surface missing proof fields, wrong validation vocabulary, mutation contradictions, and a suggested next prompt or issue-comment draft. Task kind can be auto-detected or selected explicitly.

Project registry (#427)

Versioned registry file: webui/data/projects.registry.json (schema version 1).

Override path with WEBUI_PROJECT_REGISTRY when operators keep a machine-local copy outside git. The registry stores repo identity, remotes, profile names, workflow/schema path references, and onboarding checklist steps — never tokens or credentials.

Seed entry: Gitea-Tools on https://gitea.prgs.cc with prgs-author, prgs-reviewer, and prgs-reconciler profiles.

Prompt library (#428)

Prompts are generated at load time from canonical workflow files under skills/llm-project-workflow/workflows/. SHA-256 hashes are computed from WEBUI_REPO_ROOT (defaults to the repository root). Prompt bodies are short copy/paste starters; canonical workflow files remain the only full policy source.

Live queue dashboard (#429)

/queue loads open PRs and issues for the default registry project (seed: Gitea-Tools on https://gitea.prgs.cc) using existing gitea_auth read credentials. The UI surfaces pagination proof (returned count, pages fetched, has_more, inventory_complete) and classification badges (claimed, blocked, in-review, duplicate) when evidence exists.

If credentials are missing or the fetch fails, the page shows an explicit error instead of an empty queue (fail closed).

Gated actions (#434)

/actions registers future write actions (claim, comment, review, merge, delete branch, create PR/issue). Each entry declares the MCP tool, required permission, and profile role from task_capability_map.py — aligned with gitea_resolve_task_capability. Buttons are disabled; previews always render a mutation ledger. Direct attempt_action calls fail closed without invoking MCP tools.

Worktree hygiene (#432)

/worktrees scans local branches/ directories and registered git worktrees. Each entry is classified (active-pr, active-issue, dirty, stale-clean, detached-review, unsafe-unknown, orphan). Missing preserved worktrees referenced by the issue lock file are flagged as anomalies (#404). The page includes a copy/paste canonical cleanup prompt only — no deletion actions.

Override scan root with WEBUI_REPO_ROOT (defaults to repository root).

Lease visibility (#433)

/leases surfaces read-only lease and collision state: local issue lock file, in-progress claim inventory (#268), reviewer PR lease comments when present (<!-- mcp-review-lease:v1 -->, #407), duplicate open PRs per issue (#400), and duplicate local branches per issue. Links to collision-history backend issues (#267, #268, #400, #407) are included. No lease acquire/release from UI.

Runtime health (#430)

/runtime surfaces read-only MCP/runtime diagnostics for the default registry project: active profile and role kind, authenticated identity (when credentials are available), config model/mode, local vs remote master SHA sync, shell health, workflow/schema SHA-256 hashes, and stale-runtime warnings when the checkout is behind merged safety-gate changes. Restart guidance links to #420; no tokens or MCP restart actions are exposed.

Deployment boundary (#435)

MVP serves on loopback by default. Binding 0.0.0.0 or :: is refused unless WEBUI_ALLOW_PUBLIC_BIND=1. Non-loopback hosts log a warning unless WEBUI_ALLOW_REMOTE_BIND=1. GET /health exposes deployment metadata.

Tests

pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_gated_actions.py -q
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_audit.py tests/test_webui_worktree_hygiene.py -q
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_lease_visibility.py tests/test_webui_runtime_health.py -q

pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_deployment_boundary.py -q

## Tests (#436)

Run the full hermetic web UI suite (all `test_webui_*.py` modules):

```bash
./scripts/test-webui

CI / Jenkins multibranch can call the path-filtered gate (runs only when the diff touches webui/, tests/test_webui_*, or web UI docs/scripts):

./scripts/ci-webui-check
WEBUI_CI_FORCE=1 ./scripts/ci-webui-check   # always run

scripts/test-webui sets WEBUI_TEST_OFFLINE=1 by default. In that mode the queue, lease, and runtime routes use empty offline snapshots instead of Gitea credentials, so CI can run without MCP daemon credential access. Set WEBUI_TEST_OFFLINE=0 only when deliberately validating live fetch behavior.

Or invoke unittest directly:

python3 -m unittest discover -s tests -p 'test_webui_*.py' -q

Lease visibility (#433)

/leases surfaces read-only lease and collision state: local issue lock file, in-progress claim inventory (#268), reviewer PR lease comments when present (<!-- mcp-review-lease:v1 -->, #407), duplicate open PRs per issue (#400), and duplicate local branches per issue. Links to collision-history backend issues (#267, #268, #400, #407) are included. No lease acquire/release from UI.

Runtime health (#430)

/runtime surfaces read-only MCP/runtime diagnostics for the default registry project: active profile and role kind, authenticated identity (when credentials are available), config model/mode, local vs remote master SHA sync, shell health, workflow/schema SHA-256 hashes, and stale-runtime warnings when the checkout is behind merged safety-gate changes. Restart guidance links to #420; no tokens or MCP restart actions are exposed.

Tests

pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_audit.py tests/test_webui_worktree_hygiene.py -q
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_lease_visibility.py tests/test_webui_runtime_health.py -q