feat(webui): model usage, token cost, latency, and performance analytics (Closes #651) #876
Labels
Clear labels
allocator
anti-stomp
architecture
bug
chore
codex
concurrency
contamination
control-plane
dashboard
database
design
documentation
enhancement
gitea
glitchtip
important
incident
incident-bridge
integration
jenkins
labels
leases
mcp
mcp-health
mcp-menu
multi-project
mutating
nice-to-have
observability
portability
preflight
protected-branch
queue
read-only
reconnect
recovery
refactor
release
reliability
resumable-review
reviewer
roadmap
safety
security
self-hosted
sentry
stale-runtime
status:blocked
status:in-progress
status:pr-open
status:ready
terminal-lock
testing
tracker
type:bug
type:feature
type:feature
type:guardrail
visibility
workflow
workflow-hardening
workflow-hardening
Controller-owned work allocator
Prevent concurrent LLM session stomping
Architecture / structural design
OpenAI Codex client / workflow session surface
Concurrent session safety
Workflow or session contamination incident
MCP control-plane coordination and allocation authority
MCP operational dashboard/queue view
Internal coordination storage (SQLite/Postgres)
Design / investigation, no implementation
Docs / runbooks
New feature or improvement
Gitea MCP workflow
GlitchTip integration
Operational or process incident requiring durable audit trail
Sentry-to-Gitea incident bridging
Integration testing
Jenkins integration
Label taxonomy management
Lease adopt/release/expire lifecycle
MCP server / tooling
MCP namespace and runtime health
MCP menu surface
Work spanning multiple monitoring projects or Gitea repos
Mutating action; requires gating
Observability, metrics, traces, error reporting
Cross-platform / portability
Shared preflight gates before mutation
Protected branch / stable-branch policy concern
Work queue visibility and allocation
Read-only, no mutation
MCP client reconnect/reload recovery path
Recovery paths for stale/foreign leases
Code refactor / restructure
Release / versioning
Reliability / failure handling
Persist and resume prepared review verdicts across sessions
Reviewer workflow tooling
Roadmap / umbrella issue
Safety rails and fail-closed mutation guards
Security / trust boundary
Self-hosted infrastructure integration
Sentry error monitoring integration
Stale backend daemon / runtime-vs-master parity failures
Issue is blocked
Issue is being worked on
Issue has an open pull request
Issue is ready for work
Terminal review lock (#332) path
Tests / test coverage
Issue tracker hygiene / meta
Bug or defect
Feature or enhancement
Feature or enhancement
Safety gate or guardrail
Workflow state visibility for LLMs/operators
Cross-tool workflow
LLM workflow coordination hardening
LLM workflow coordination hardening
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Scaled-Tech-Consulting/Gitea-Tools#876
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Closes #651
This PR implements Phase 4 Web Console analytics for model usage, token cost, latency percentiles, and workflow-stage performance (
/analyticsand/api/v1/analytics).Changes
usage_events): Addedusage_eventstable and indexes tocontrol_plane_db.py(migrated to schema version 5) with support for recording and querying model usage events.webui/analytics_loader.py): Implemented aggregation logic across 5 dimensions (Project, Role, Model, Issue/PR, Stage) with percentile calculation (p50, p90, p95, p99) and explicit "Unknown" labels for missing metrics to prevent zero-fabrication.webui/analytics_views.py): Added HTML view and dashboard cards for model usage, latency percentiles, cost estimates, and workflow stage duration.console_redaction.py) applied to metadata and prompt summaries before persistence and output emission./analytics(HTML view),/api/v1/analytics(JSON view),/api/analytics(unversioned alias), and/api/v1/analytics/usage(HTTP ingestion endpoint).docs/observability/analytics-instrumentation.mdand complete test coverage intests/test_webui_analytics.py.Verification
Ran test suite: 382 passed.
Canonical PR State
STATE: awaiting-review
WHO_IS_NEXT: reviewer
NEXT_ACTION: Review pull request #876 against Issue #651 acceptance criteria for model usage, token cost, latency percentiles, and workflow stage performance analytics views/APIs.
NEXT_PROMPT: Review pull request #876 as prgs-reviewer; verify schema version 5 migration, percentiles computation, secret redaction, non-zero-fabrication of missing metrics, HTML dashboard rendering, and 382 unit tests passing.
WHAT_HAPPENED: Authored, verified, committed, pushed, and opened PR #876 for Phase 4 Web Console analytics (Issue #651).
WHY: Delivers model usage, token cost, latency percentiles, and workflow stage performance tracking for the Web Console epic (#631).
RELATED_PRS: #876
BLOCKERS: none
VALIDATION: Ran unit test suite (382 tests passed).
LAST_UPDATED_BY: jcwalker3 / prgs-author
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: #651
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 43331-f40882b73ee5
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr876-issue-651
phase: claimed
candidate_head:
5c5c1fdf77target_branch: master
target_branch_sha: none
last_activity: 2026-07-24T11:56:58Z
expires_at: 2026-07-24T12:06:58Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: #651
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 43331-f40882b73ee5
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr876-issue-651
phase: released
candidate_head:
5c5c1fdf77target_branch: master
target_branch_sha: none
last_activity: 2026-07-24T11:59:24Z
expires_at: 2026-07-24T12:09:24Z
blocker: manual-release
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: #651
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 43331-61aaac7dfdd0
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr876-issue-651
phase: claimed
candidate_head:
1f144705e2target_branch: master
target_branch_sha: none
last_activity: 2026-07-24T11:59:36Z
expires_at: 2026-07-24T12:09:36Z
blocker: none
Canonical PR State
STATE: CHANGES_REQUESTED
WHO_IS_NEXT: author
NEXT_ACTION: Fix F1 (stored XSS) and F2 (unauthenticated DB write) blockers, add escaping + authz tests, push, then request re-review.
NEXT_PROMPT:
WHAT_HAPPENED: Native reviewer pass against Issue #651 acceptance criteria. Aggregation/percentile logic and AC#3/AC#4 are sound, but two HIGH security blockers are present at the current head. A prior pass at
5c5c1fdwas invalidated by an author base-sync push mid-review; both blockers were re-verified present at1f144705.WHY: F1 renders every dynamic analytics field unescaped (stored XSS in the operator console); F2 exposes the first unauthenticated write path in an otherwise read-only console, which also supplies the F1 injection vector.
ISSUE: #651
HEAD_SHA:
1f144705e2REVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: NO
BLOCKERS: F1 stored XSS (webui/analytics_views.py); F2 unauthenticated DB write POST /api/v1/analytics/usage (webui/app.py)
VALIDATION: Native prgs-reviewer review; F1 confirmed by reading analytics_views.py @1f144705 (no import html / no escaping); F2 confirmed by reading app.py @1f144705 (api_v1_analytics_ingest calls record_usage with no _authorize_request). Base 0 behind, mergeable, no conflicts.
LAST_UPDATED_BY: prgs-reviewer
NATIVE_REVIEW_PROOF: prgs-reviewer reviewed PR #876 at head
1f144705e2against Issue #651 acceptance criteria; findings below are head-scoped.Review Ledger
webui/analytics_views.pywebui/app.pyPOST /api/v1/analytics/usagewrites DB with zero authzconsole_authzor drop HTTP writeusage_eventswebui/analytics_loader.pyload_analyticspasses possibly-Noneremote/org/repo into str fieldsF1 — HIGH — Stored XSS in
webui/analytics_views.py_render_group_tableinterpolates{key}(model / role / stage / project_id) and_render_events_tableinterpolates{e.model}/{e.role}/{e.stage}/{e.status}/ work-item fields directly into HTML with no escaping. This breaks the webui-wide convention:audit_views.py,project_views.py,runtime_views.py, andgated_action_views.pyall render dynamic content throughhtml.escape(..., quote=True).redact_payload/redact_textperform secret redaction only — not HTML-escaping — and only touchmetadata, so they do not mitigate this. A craftedmodel/role/stagevalue (injectable via the unauthenticated ingest path in F2) executes in the operator's authenticated console session.Fix: add
_escape = functools.partial(html.escape, quote=True)and wrap every interpolated field, mirroringaudit_views.py.F2 — HIGH — Unauthenticated DB write:
POST /api/v1/analytics/usageapi_v1_analytics_ingest(webui/app.py) writes tocontrol_plane_db(viarecord_usage) with zero authorization. The webui is otherwise a read-only MVP:_READ_ONLY_METHODS = {GET, HEAD, OPTIONS},/auditPOST returns 404, and gated actions route throughconsole_authz._authorize_request(#633 RBAC) — seeapi_action_attempt, which calls_authorize_request(..., for_execution=True). This ingest route registers a POST and so bypasses the 405 read-only guard entirely; it never callsauthorize(), violating the Issue #651 RBAC acceptance criterion. It also supplies the injection vector for F1.Fix: gate the endpoint through
console_authz, or remove the HTTP write entirely and ingest only via the in-processrecord_usagepath.F3 — MEDIUM — No retention limits
Issue #651 security requirements list retention limits.
usage_eventsgrows unbounded and, combined with the unauthenticated write in F2, enables DoS-by-volume.F4 — LOW —
Nonefields intoAnalyticsSnapshotload_analyticscan passNoneremote/org/repo intoAnalyticsSnapshotstring fields. Cosmetic; guard or coerce to""/"Unknown".What is correct (not blocking)
metadata) satisfied.tests/test_webui_analytics.py(8 tests) cover percentile/aggregate/redaction/routes/ingest.Test gaps
No escaping test (F1) and no authz test (F2). Add both alongside the fixes.
Please address F1 and F2 (blockers) before re-review. F3/F4 may be folded into the same revision. Branch is currently even with
master(0 behind) — keep it current through the fix so re-review runs against a current base.repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: #651
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 43331-61aaac7dfdd0
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr876-issue-651
phase: released
candidate_head:
1f144705e2target_branch: master
target_branch_sha: none
last_activity: 2026-07-24T12:02:57Z
expires_at: 2026-07-24T12:12:57Z
blocker: manual-release
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: none
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 62500-a82a2fe61715
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-651-usage-cost-analytics
phase: claimed
candidate_head:
38506a753ftarget_branch: master
target_branch_sha: none
last_activity: 2026-07-24T13:30:17Z
expires_at: 2026-07-24T13:40:17Z
blocker: none
Canonical PR State
STATE: APPROVED
WHO_IS_NEXT: merger
NEXT_ACTION: Merge PR #876 into master and close #651.
NEXT_PROMPT:
WHAT_HAPPENED: Re-reviewed PR #876 at head
38506a7against Issue #651 acceptance criteria. Prior REQUEST_CHANGES blockers (review_id 554) are all remediated, each with a dedicated regression test. Re-verified F1 and F2 by reading the files at head38506a7.WHY: All four prior findings are fixed at the current head with regression coverage; scope is limited to Issue #651; read-only console posture is intact, so the PR meets the approve bar.
ISSUE: #651
HEAD_SHA:
38506a753fREVIEW_STATUS: APPROVE
MERGE_READY: YES
BLOCKERS: none
VALIDATION: Read webui/analytics_views.py and webui/app.py at head
38506a7confirming F1 (html.escape on every dynamic field) and F2 (authorize record_analytics_usage for_execution=True; 403 fail-closed before record_usage). Prior session executed tests/test_webui_analytics.py -> 9 passed, including the 4 regression tests below. PR body reports full suite 382 passed.LAST_UPDATED_BY: prgs-reviewer
NATIVE_REVIEW_PROOF: prgs-reviewer reviewed PR #876 at head
38506a753fagainst Issue #651 acceptance criteria; all prior REQUEST_CHANGES blockers (review_id 554) remediated with regression tests.Review Ledger
AC1-4 met; read-only console posture intact. Focused suite: 9 passed.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: #651
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 65113-a2ca67fde314
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr876-20260724093923
phase: claimed
candidate_head:
22f9547fd8target_branch: master
target_branch_sha:
2e4ed38c51last_activity: 2026-07-24T13:40:31Z
expires_at: 2026-07-24T13:50:31Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: #651
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 65113-a2ca67fde314
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr876-20260724093923
phase: released
candidate_head:
22f9547fd8target_branch: master
target_branch_sha:
2e4ed38c51last_activity: 2026-07-24T13:40:39Z
expires_at: 2026-07-24T13:50:39Z
blocker: manual-release
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #876
issue: none
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 62500-931a928ce0e8
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-651-usage-cost-analytics
phase: claimed
candidate_head:
22f9547fd8target_branch: master
target_branch_sha: none
last_activity: 2026-07-24T13:41:08Z
expires_at: 2026-07-24T13:51:08Z
blocker: none
Canonical PR State
STATE: APPROVED
WHO_IS_NEXT: merger
NEXT_ACTION: Merge PR #876 into master and close #651.
NEXT_PROMPT:
WHAT_HAPPENED: Base-sync merged live master into the PR branch (former approved head
38506a7-> 22f9547; PR now 0 behind, branch-protection-current-base satisfied). Re-reviewed at the new head22f9547. F1 and F2 re-verified by reading webui/analytics_views.py and webui/app.py at22f9547— both files are byte-identical to the approved38506a7(blob shas506aa96andf7648eb), so the base-sync introduced no change to the remediated code.WHY: All four prior findings remain fixed at the current head with regression coverage, scope is limited to Issue #651, read-only console posture intact, and the branch now contains current base — the PR meets the approve and merge-ready bar.
ISSUE: #651
HEAD_SHA:
22f9547fd8REVIEW_STATUS: APPROVE
MERGE_READY: YES
BLOCKERS: none
VALIDATION: assess_pr_sync_status reports commits_behind=0, mergeable=true, has_conflicts=false at head
22f9547. get_file at22f9547confirms F1 (_escape=html.escape(str(x),quote=True) on every dynamic field) and F2 (authorize record_analytics_usage for_execution=True; allowed=bool(decision.allowed and decision.execution_enabled); 403 fail-closed before record_usage). Files byte-identical to prior-approved38506a7. Prior session executed tests/test_webui_analytics.py -> 9 passed; PR body reports full suite 382 passed.LAST_UPDATED_BY: prgs-reviewer
NATIVE_REVIEW_PROOF: prgs-reviewer reviewed PR #876 at head
22f9547fd8against Issue #651 acceptance criteria; all prior REQUEST_CHANGES blockers (review_id 554) remediated with regression tests; base-sync from38506a7introduced no change to remediated files.Review Ledger
AC1-4 met; read-only console posture intact. Focused suite: 9 passed.