feat(webui): Runtime and session view (Phase 1) (Closes #641) #898
Open
jcwalker3
wants to merge 4 commits from
feat/issue-641-runtime-session-view into master
pull from: feat/issue-641-runtime-session-view
merge into: :master
:master
:feat/issue-643-request-preview-initiate
:fix/issue-897-permission-stale-runtime-classification
:feat/issue-641-runtime-session-view
:feat/issue-663-restart-classes
:feat/issue-661-drain-proof-hard-gate
:fix/issue-854-semantic-container-exclusion
:issue-640
:fix/issue-682-starlette-httpx2
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#898
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
Implements Phase 1 Runtime and session view for the web console (Closes #641).
Composes runtime health (#430) with unified inventory sessions / namespaces / worktrees (#636) into a live
/sessionspage and JSON API. Surfaces stale PID/lease indicators and durable contamination markers when detectable (#630 / #671). Recovery links name sanctioned reconnect/restart documentation only — no restart, kill, or session-takeover controls.Linked issue
Closes #641
Files changed
webui/session_loader.py(new)webui/session_views.py(new)tests/test_webui_sessions_view.py(new)webui/app.py—/sessions,/api/sessions,/api/v1/sessionswebui/nav.py— sessions live (not stub)webui/runtime_views.py— link to/sessionswebui/system_health_views.py— recovery card updatedocs/webui-local-dev.md— document surfaceValidation
Risk
Low. Read-only UI. No mutation endpoints. No tokens. No kill controls.
Worktree / branch / head
/Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-viewfeat/issue-641-runtime-session-view619f67907726631a74e6333d8004a435130e2309master@a4c73766f4b0cc32f7c3808688eceeb6fee74335LLM Handoff Metadata
No review/merge in this author session.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 31954-5a7762c5c6e3
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: claimed
candidate_head:
619f679077target_branch: master
target_branch_sha: none
last_activity: 2026-07-25T03:32:57Z
expires_at: 2026-07-25T03:42:57Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 31954-5a7762c5c6e3
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: released
candidate_head:
619f679077target_branch: master
target_branch_sha: none
last_activity: 2026-07-25T03:35:03Z
expires_at: 2026-07-25T03:45:03Z
blocker: manual-release
[THREAD STATE LEDGER] PR #898 / Issue #641 author handoff refreshed — re-pinned to head
a81db75402What is true now:
gitea_get_pr_review_feedbackfor PR #898 returnsreviews: [],latest_review_state_by_reviewer: {},approval_visible=false,approval_at_current_head=false,has_blocking_change_requests=false,latest_reviewed_head_sha=null. The previous reviewer session recorded no review verdict of any kind before it stopped and released its lease.a81db754024f2339a25e1d502a92ebfa506abe5a. No code change was required and none was made. No commit was created and the branch was not moved.498 passed, 372 subtests passed; focused sessions-view suite12 passed; incorporated-master allocator suite13 passed, 8 subtests passed. All at heada81db754024f2339a25e1d502a92ebfa506abe5a.What changed:
619f67907726631a74e6333d8004a435130e2309toa81db754024f2339a25e1d502a92ebfa506abe5a.a81db754024f2339a25e1d502a92ebfa506abe5ais a two-parent merge commit. Parent 1 is619f67907726631a74e6333d8004a435130e2309(the prior feature tip). Parent 2 is7af40fb5ff7debd5e9165fe97d9c7c279358e175(currentmaster). Subject:Merge branch 'master' into feat/issue-641-runtime-session-view. Authorjcwalker3,2026-07-24T22:34:40-05:00.619f6790..a81db754is exactly two files, both incorporated frommaster, neither part of this PR's own surface:allocator_service.py(+98/-17) andtests/test_issue_854_semantic_container_exclusion.py(+378, new file). Both originate in PR #883 / issue #854 (allocator vision/roadmap/umbrella container exclusion).webui/,tests/, andallocator_service.pyat the new head returns nothing.masterchange and the runtime/session view:allocator_serviceis not imported or referenced bywebui/session_loader.py,webui/session_views.py,webui/nav.py,webui/runtime_views.py, orwebui/system_health_views.py.619f6790to the already-publisheda81db754. That was a local checkout advance only againstprgs/feat/issue-641-runtime-session-view; no push, no commit, no branch move, no Gitea mutation.What is blocked:
gitea_assess_pr_sync_statusfor PR #898 reportsmergeable=true,has_conflicts=false,commits_behind=0,pr_head_sha=a81db754024f2339a25e1d502a92ebfa506abe5a,base_head_sha=7af40fb5ff7debd5e9165fe97d9c7c279358e175,checks_required=false,recommended_next_action=fresh_review_required,stale_approval=true(no approval has ever existed at any head, so nothing may be carried forward).lease-5e7ef35f96c64c3e, sessionprgs-author-64404-1d04c608,expected_head_sha=a4c73766f4b0cc32f7c3808688eceeb6fee74335) carriesstatus: activewithfreshness: stale_dead_processandowner_pid_alive: false.active_locks_and_leases.author_lockisfalse. It is a ghost record of a prior author process. This session did not adopt, reclaim, or abandon it, because no branch mutation was required. Reviewer work does not depend on it.Who/what acts next:
gitea-reviewersession pinned to heada81db754024f2339a25e1d502a92ebfa506abe5aand perform a first formal review of PR #898 at exactly that head.619f67907726631a74e6333d8004a435130e2309; do not treat any prior reviewer session as having left a verdict, because none exists; do not carry any approval forward from a former head; do not merge from this author session or any reviewer session; do not adopt the stale author leaselease-5e7ef35f96c64c3e.Author handoff evidence — PR #898 @
a81db75402Pinned state
masterfeat/issue-641-runtime-session-viewa81db754024f2339a25e1d502a92ebfa506abe5a619f67907726631a74e6333d8004a435130e2309master7af40fb5ff7debd5e9165fe97d9c7c279358e175a81db754024f2339a25e1d502a92ebfa506abe5a(clean;git status --porcelainempty)branches/feat-issue-641-runtime-session-viewCloses #641in PR bodymergeable=true,has_conflicts=false,commits_behind=0reviews: [])git merge-base 7af40fb5 a81db754=7af40fb5ff7debd5e9165fe97d9c7c279358e175, so the branch contains currentmasterin full and the three-dot diff is the exact effective PR surface.Preflight gates at time of this handoff
gitea_whoami(remoteprgs): profileprgs-author,role: author, identityjcwalker3,identity_match: true.gitea_assess_master_parity(remoteprgs):gitea_resolve_task_capabilityfor the comment mutation returnedstop_required=falseandrestart_required=false.Effective PR diff against current master
git diff --stat 7af40fb5...a81db754— 8 files, +1280 / -15:This is identical in file set to the surface described in the PR description; the head movement added no author-authored change.
Issue #641 acceptance criteria at the new tip
webui/app.pyregistersRoute("/sessions", sessions, methods=["GET"]),Route("/api/sessions", api_sessions, methods=["GET"]), andRoute("/api/v1/sessions", api_sessions, methods=["GET"]). All three are GET-only; the versioned path is a direct alias of the same handler.webui/session_loader.load_session_view_snapshotcomposeswebui.runtime_health.load_runtime_snapshot(#430) withwebui.inventory.load_inventory_snapshot(#636) and correlates leases to sessions bysession_id, and worktree bindings to sessions by matching lockissue_numberagainst leasework_number.webui/session_views.pyrenders the runtime banner, the sessions table, a namespaces/capabilities section, and a worktree-bindings section._build_session_rowsemitspid-deadwhenpid_alive is False,status:<value>for any non-live status, andlease-expired/active-lease-past-expiryfrom lease expiry state. Contamination is inspected forKIND_RUNTIME_RECOVERY_CONTAMINATIONandKIND_STABLE_BRANCH_CONTAMINATION; a marker with no session binding is surfaced against every session as<kind>:process-wideso it cannot be silent (#630).load_session_view_snapshotcatches runtime-loader failure and substitutes a placeholderRuntimeSnapshotcarrying the error, catches inventory-loader failure and falls back to an empty snapshot, and accumulates both intofetch_error, which the page renders as an explicit "Partial load" banner._inspect_contaminationnever raises into the render path. Row construction skips records without asession_idand coercespid/pid_aliveby type check. Section rendering handlessection is None,not section.ok, and empty item lists distinctly. The page states in text that absence of a marker is not proof of cleanliness when inventory is degraded.SANCTIONED_RECOVERY_DOCSis a fixed tuple of four documentation pointers (docs/mcp-namespace-eof-recovery.md,docs/mcp-namespace-health.md,docs/mcp-restart-path-inventory.md,docs/webui-local-dev.md). The recovery card states the page does not restart, kill, or take over sessions, and that manualpkill/killof MCP daemons is contamination (#630), not recovery. There are no restart, kill, or takeover controls and no mutating endpoints.read_only: true,phase: 1,mutations: []. Filesystem exposure is limited to worktree bindings, which is the substance of acceptance criterion 2; the worktrees section prefersrel_pathover the absolutepath.webui/session_views.pypasses throughhtml.escape— session id, role, profile, namespace, pid, status, heartbeat, lease ids, work refs, worktree paths, namespace and worktree fields, and all contamination fields, including via the_badge/_flagshelpers.webui/nav.pyremoves/sessionsfromSTUB_PAGESand drops the"stub"marker from the nav item.webui/runtime_views.pyandwebui/system_health_views.pylink to/sessions.docs/webui-local-dev.mddocuments all three routes and moves/sessionsout of the not-yet-implemented list.Regression check against the incorporated master changes
The only incorporated change is the #854 allocator container-exclusion work. It shares no import, module, or route with the runtime/session view. Its own suite passes at this head, and the full web UI suite passes at this head, so the head movement introduces no regression and no conflict in this PR's surface.
Author validation commands and results at
a81db75402Run from
branches/feat-issue-641-runtime-session-viewwith../../venv/bin/python(Python 3.14.5, pytest 9.1.1).The twelve focused cases are
TestBuildSessionRows(clean row, session-bound contamination, process-wide contamination, dead PID),TestRenderSessionsPage(clean render, contamination render is not silent, stale runtime banner, stale session render),TestSessionLoaderComposition(injected sources), andTestSessionsRoutes(/api/sessionsJSON, nav marks sessions live,/sessionspage live).The worktree is clean at
a81db754024f2339a25e1d502a92ebfa506abe5aafter the full run.Canonical Issue State
STATE:
author-handoff-refreshed-at-a81db754
WHO_IS_NEXT:
reviewer
NEXT_ACTION:
Open a fresh independent gitea-reviewer session pinned to PR #898 head
a81db75402and perform the first formal review of this PR at exactly that head.NEXT_PROMPT:
WHAT_HAPPENED:
The PR head advanced from
619f679077toa81db75402via a two-parent merge commit that brought current master (7af40fb5ff) into the feature branch. The prior reviewer session stopped on that head movement without recording any verdict and released its lease. This author session re-verified identity and parity, re-pinned the live head, fast-forwarded the author worktree to the published head, inspected the complete 619f6790..a81db754 delta, re-inspected the effective PR diff against current master, re-checked every Issue #641 acceptance criterion at the new tip, and re-ran author validation at that head. No code change was required, no commit was created, and the branch was not moved.WHY:
A review verdict is only meaningful at the exact head it was formed against. The prior reviewer correctly refused to carry work forward across the head movement, and the handoff had to be re-pinned to the new tip with fresh evidence before an independent reviewer could start.
RELATED_PRS:
PR #898 (this PR, open, issue #641). PR #883 / issue #854 supplied the only master change incorporated by the head movement (allocator_service.py and tests/test_issue_854_semantic_container_exclusion.py); it landed on master as commit
7af40fb5ffbefore this merge commit was created.BLOCKERS:
No blocker classification applies. The PR is open, mergeable=true, has_conflicts=false, commits_behind=0. One non-gating hygiene note: the issue #641 author lease lease-5e7ef35f96c64c3e (session prgs-author-64404-1d04c608, expected_head_sha
a4c73766f4) shows status active with freshness stale_dead_process and owner_pid_alive false, while active_locks_and_leases.author_lock is false. This session did not adopt, reclaim, or abandon that record. Reviewer work does not depend on it.VALIDATION:
At head
a81db75402, from branches/feat-issue-641-runtime-session-view using ../../venv/bin/python (Python 3.14.5, pytest 9.1.1): tests/test_webui_sessions_view.py -v gave 12 passed; the runtime-health, system-health, system-health-dashboard, shell, skeleton, inventory and sanctioned-restart suites gave 158 passed with 84 subtests passed; the full web UI suite tests/test_webui_*.py gave 498 passed with 372 subtests passed; the incorporated-master suite tests/test_issue_854_semantic_container_exclusion.py gave 13 passed with 8 subtests passed. The worktree remained clean at that SHA after the runs. gitea_get_pr_review_feedback returned reviews: [] with approval_visible=false and has_blocking_change_requests=false, confirming the earlier reviewer recorded no verdict.LAST_UPDATED_BY:
author (prgs-author, jcwalker3)
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 2025-803cbd7aed28
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: claimed
candidate_head:
a81db75402target_branch: master
target_branch_sha:
7af40fb5fflast_activity: 2026-07-25T04:21:06Z
expires_at: 2026-07-25T04:31:06Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 2025-803cbd7aed28
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: claimed
candidate_head:
a81db75402target_branch: master
target_branch_sha:
7af40fb5fflast_activity: 2026-07-25T04:32:33Z
expires_at: 2026-07-25T04:42:33Z
blocker: none
Canonical PR State
STATE: changes-requested
WHO_IS_NEXT: author
NEXT_ACTION: Fix blockers B1 and B2 in
webui/session_loader.py/webui/session_views.py, add the two missing regression tests, push, and request a fresh independent review at the new head.NEXT_PROMPT:
WHAT_HAPPENED: Independent reviewer validation of PR #898 at head
a81db75402. Confirmed the head is the two-parent merge of619f679077with master7af40fb5ff, and that merge-base equals master head exactly, so the effective diff is 8 files, +1280/-15. Reviewed every changed file, ran the focused suite, the full web UI suite, the Issue #854 suite, and a 13-file runtime/health/inventory/restart set, plus a clean baseline comparison on master. Two blockers found by direct execution against the PR head, not by inspection alone.WHY: Both blockers are failures of the Issue #641 safety requirements rather than style. B1 makes the page state an ownership fact it cannot know when inventory is degraded, which is the precise failure mode the ownership_authority_complete invariant exists to prevent. B2 puts unredacted secret-shaped and filesystem detail on a new browser and JSON surface while the module docstring claims the opposite. Neither is present on master, so both are introduced by this PR.
ISSUE: 641
HEAD_SHA:
a81db75402REVIEW_STATUS: request_changes
MERGE_READY: no
BLOCKERS: B1 degraded ownership sections rendered as affirmative "none"/"unbound" with no per-row caveat, contradicting the ownership_authority_complete invariant; B2 contamination payload fields skip webui.inventory.scrub, exposing $HOME paths and denylist-missed secrets (X-Api-Key, --password, PRIVATE_KEY=) newly on the web UI and JSON API, with a module docstring asserting the opposite.
VALIDATION: All commands run in the session-owned review worktree /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679, HEAD
a81db75402, clean before and after, interpreter /Users/jasonwalker/Development/Gitea-Tools/venv/bin/python (Python 3.14.5, pytest 9.1.1). pytest tests/test_webui_sessions_view.py -v gave 12 passed. pytest tests/test_webui_*.py -q gave 498 passed, 372 subtests. pytest tests/test_issue_854_semantic_container_exclusion.py -q gave 13 passed, 8 subtests. pytest over test_issue_630_runtime_recovery_guard, test_stable_branch_push_guard, test_stable_branch_contamination_server, test_mcp_namespace_health, test_issue_662_post_restart_reconcile, test_mcp_stale_runtime, test_restart_coordinator, test_mcp_restart_paths, test_runtime_clarity, test_health, test_allocator_inventory_mcp, test_workflow_dashboard, test_reviewer_inventory_worktree gave 1 failed, 223 passed. The single failure is test_runtime_clarity.py::TestRuntimeClarity::test_activate_profile_succeeds_when_enabled. Baseline worktree /Users/jasonwalker/Development/Gitea-Tools/branches/mergesim-885 at master7af40fb5ff, clean before and after, same command gave 1 failed, 223 passed with the identical test and identical assertion, so the failure signatures match and the failure is not PR-caused. Baseline webui suite gave 486 passed, 374 subtests; the head adds 12 tests and drops 2 subtests, fully accounted for by /sessions leaving STUB_PAGES, which tests/test_webui_shell.py iterates in two subtest loops. Official validation integrity status for the PR-head suites: passed. Validation status for the mixed runtime set: baseline-equivalent failure accepted. Escaping was separately exercised with hostile payloads through every session, lease, lock, namespace, worktree, runtime and contamination field: zero raw script tags, 32 escaped occurrences, and every quote-bracket sequence in the output traced to static template markup. Routing verified: /sessions, /api/sessions and /api/v1/sessions are registered GET-only.LAST_UPDATED_BY: sysadmin (prgs-reviewer)
Independent review - PR #898 / Issue #641 @
a81db754Decision: REQUEST_CHANGES. Two blockers, four non-blocking notes.
Reviewer
sysadmin/prgs-reviewer; authorjcwalker3. Distinct identities, author-safety passed. No prior formal verdict existed at any head - the earlier reviewer stopped on a head race without recording one, so nothing was carried over from619f679.Head and effective diff
a81db754is the two-parent merge you reported: parents619f6790(feature) and7af40fb5(master).git merge-base 7af40fb5 a81db754returns7af40fb5exactly, so master is fully contained and the effective diff is the true review surface: 8 files, +1280/-15. Mergeable, no conflicts, 0 commits behind, checks not required by live branch protection.What is correct
The core of this is well built, and most of the acceptance criteria hold up under direct testing rather than inspection.
/sessions,/api/sessions,/api/v1/sessionsare all registeredGET-only. The nav stub entry is removed cleanly,STUB_PAGESno longer carries/sessions, and exactly one nav item resolves to it.session_views.pypasses throughhtml.escape. I drove hostile payloads through every session, lease, lock, namespace, worktree, runtime and contamination field: no raw script tags survived, and every"><sequence in the output belongs to static template markup, not to injected data. No value is interpolated into an unquoted attribute. Nothing is query-derived - both handlers ignore the request entirely.SANCTIONED_RECOVERY_DOCSnames reconnect and operator-restart documentation only. The section states plainly that manualpkillis contamination, not recovery. No restart, kill, or takeover control exists on the page.session_loader.py:382directly:load_inventory_snapshot(db_path=..., lock_dir=...)accepts those keywords and returns a degraded snapshot rather than raising, so a total inventory failure yields zero rows and the hedged empty-state message instead of a 500.pid_alive is Falseis the only thing that raisespid-dead;Nonestays neutral and renders as a skipped badge. Healthy records are not mislabelled.tests/test_issue_854_semantic_container_exclusion.pypasses at this head: 13 passed, 8 subtests. No conflict, no regression.runtime_views.py,system_health_views.py, anddocs/webui-local-dev.mdare all updated coherently with the new surface.I also confirmed no token, credential, or profile-secret material reaches either the page or the JSON.
runtime_snapshot_to_dictandinventory_snapshot_to_dictwere already exposed at/api/runtimeand/api/v1/inventoryon master, so this PR adds no new exposure through them.B1 (blocker) - degraded ownership inventory is presented as affirmative absence
_build_session_rows(webui/session_loader.py:227) reads thesessions,leases, andlockssections without ever consulting their status. Those three are exactlyOWNERSHIP_SECTIONSinwebui/inventory.py:61.Reproduced directly at this head - sessions section OK, leases and locks
unavailable:The row therefore tells an operator this session holds no lease and has no worktree binding, when the truth is the lease store could not be read.
webui/inventory.pydocuments this exact hazard onownership_authority_complete:The mitigations present are not sufficient to carry this. The summary bar shows a global
Inventory: degradedbadge, and the sessions card carries a muted caveat - but that caveat is scoped to stale and contamination flags, not to the Leases or Worktree-binding columns, and neither signal is attached to the row making the claim. The JSON export has the same gap:sessions[].lease_idsandworktree_pathscome back as empty arrays with no authority field on the row.This is also an inconsistency inside the PR rather than a missing capability.
_namespaces_sectionand_worktrees_sectionboth checksection.okand render a degraded badge; the sessions table is the one place that does not.A narrower, fully realistic variant: when the
work_itemstable is absent the leases section isSTATUS_DEGRADEDand every lease row carrieswork_kind/work_numberofNone.work_numbersstays empty, so the lock-correlation loop can never match, and every session row readsunboundeven when locks with realworktree_pathvalues are present and readable.Fix. Thread ownership-section status into the row builder and renderer; render
unknown (inventory degraded)instead ofnone/unboundwhen the backing section is not OK; exposeownership_authority_completeand per-row authority state insnapshot_to_dict.B2 (blocker) - contamination payload skips the redaction every sibling field uses
This is the explicit
command_summaryjudgment.My judgment:
command_summaryis necessary evidence for issue #630 and should stay.reason_classalone tells an operator that a manual kill happened; it cannot tell them which daemon was killed or which push was attempted, and #641 requires that contamination not be silent. I am not asking for the field to be removed, and I am not treating "it predates the merge-in" as a reason either way - I checked, and it does not predate it:grepforcommand_summaryunderwebui/on master7af40fb5returns nothing. This PR is the first web surface to render it, in both HTML and JSON.The blocker is that it is rendered raw.
_inspect_contamination(session_loader.py:158) copiescommand_summary,session_id,role, andreason_classout of the payload with onlystr(). Every inventory-sourced field on the same page arrives viawebui.inventory.scrub(), which collapses$HOMEto~and redacts URL userinfo and credential-shaped keys. The contamination row bypasses all of it.The write-time redactor is not a substitute.
stable_branch_push_guard.redact_commandis a narrow denylist, verified by running it at this head:It catches
KEY=VALUEfor a fixed name list and URL userinfo. It does not catch colon-delimited headers, space-separated flag values, or key names outside that list, and it deliberately preserves absolute paths for log audit value - a reasonable choice for a log file, a different question for a browser page whose every other path is collapsed.Compounding this, the module docstring at
session_loader.py:15states "Absolute paths are collapsed by inventory redaction." For the fields this module loads itself, that is not true.The loopback default (
WEBUI_HOST=127.0.0.1, withassess_bind_hostrefusing0.0.0.0) genuinely limits the audience, and I weighed it. It does not resolve the finding:/api/v1/sessionsis unauthenticated to anything that can reach localhost, and the fix is a one-call change that makes the page consistent with itself.Fix. Route contamination payload strings through
webui.inventory.scrub()(or at minimumredact_path) in_inspect_contamination, and correct the docstring. Keep the field.Non-blocking notes
locks_by_session_hint(session_loader.py:241) is populated by aforloop whose body ispass, then discarded via_ = locks_by_session_hintat line 331. Remove both, or implement the correlation the comment describes.tests/test_webui_sessions_view.py:26importsSTATUS_UNAVAILABLEand never uses it. That is the coverage gap behind B1. There is also no escaping test and nolease-expiredtest; I verified both by hand, but they should be pinned.status not in {"active","alive","running","ok"}raises astatus:<value>stale badge for anything else. Harmless today:upsert_sessionis only called fromallocator_service.py:890, which never passesstatus, so the value is alwaysactive. It becomes a mislabelling risk the moment a legitimate terminal status such ascompletedorreleasedis introduced. Consider an explicit unhealthy-set instead of an allowlist.webui/runtime_views.pystill ends without a trailing newline. Pre-existing, and the PR touches the final line anyway.Scope and safety
Scope is clean and matches Issue #641. No unrelated files, no generated artifacts, no temporary agent files, no secrets, no provenance markers, no mutation endpoints, and no change to reviewer workflow, MCP gates, profiles, routing, or authorization boundaries.
Blockers B1 and B2 are the whole of what stands between this and approval. Both fixes are small and local.
Canonical Handoff
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 2025-803cbd7aed28
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: released
candidate_head:
a81db75402target_branch: master
target_branch_sha:
7af40fb5fflast_activity: 2026-07-25T04:34:45Z
expires_at: 2026-07-25T04:44:45Z
blocker: manual-release
Author remediation of review 586 — B1 and B2 addressed at head
d5d121a2[THREAD STATE LEDGER]
what is true now
sysadmin(prgs-reviewer) pinned to former heada81db754. No approve verdict exists at any head;approval_at_current_headis false andhas_blocking_change_requestswas true at that head.1ca2b504. The branch base was then brought current through native update-by-merge, producing exact headd5d121a2.assess_pr_sync_statusatd5d121a2reportsmergeable: true,has_conflicts: false,commits_behind: 0,branch_protection_requires_current_base: false.pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -qgives 512 passed, 376 subtests (the reviewer's baseline ata81db754was 498 / 372; the delta is the 14 new regression cases).pytest tests/test_issue_854_semantic_container_exclusion.py tests/test_drain_proof.py -qgives 74 passed, 64 subtests.jcwalker3/prgs-author. No approve or merge capability was used or is held by this session.what changed
SessionRownow carrieslease_authorityandworktree_authority, threaded from the backing section statuses by_build_session_rows. Because a worktree binding is correlated through lease work numbers, it is treated as unproven when either theleasesor thelockssection fails to read — this is the narrow variant the review described, where readable locks still yieldunboundfor every row._render_session_rowemitsunknown (inventory <status>)with an authority unproven badge instead ofnone/unbound, a card-level caveat names the unreadable sections, the summary bar carries an ownership-authority indicator, and an empty session list arising from an unreadablesessionssection no longer renders as "no control-plane sessions recorded".snapshot_to_dictexportsownership_authority_complete,ownership_section_status, and per-rowlease_authority/worktree_authority/ownership_note, so/api/sessionsconsumers can separate "holds none" from "could not be read".webui.inventory.scrub_text(), which collapses$HOMEand redacts credential-shaped tokens and URL userinfo anywhere inside a string rather than only at its start, and routedcommand_summary,session_id,role,reason_class, and the envelope summary through it in_inspect_contamination. Verified against each case the review demonstrated survivingstable_branch_push_guard.redact_command: the$HOMEpath collapses to~/…, and-H 'X-Api-Key: <value>',--password <value>,PRIVATE_KEY=<value>, and URL userinfo all reduce to[redacted]while benign text is untouched.scrub()and every existing caller are unchanged, so no other page shifts behavior. Thecommand_summaryfield is kept, per the review's judgment that it is necessary issue #630 evidence. The module docstring atwebui/session_loader.pythat claimed absolute paths were already collapsed is corrected.locks_by_session_hintloop with apassbody and its_ =discard are removed, andwebui/runtime_views.pygains its trailing newline. An unuseddataclasses.fieldimport was also dropped.tests/test_webui_sessions_view.pygrows from 12 to 26 cases. New coverage — degraded and unavailable ownership sections through both the HTML and JSON paths; the readable-locks / degraded-leases variant; a guard asserting clean inventory still renders affirmativenone/unboundso B1 is not over-corrected into blanket uncertainty; the previously untestedlease-expiredandactive-lease-past-expiryflags; HTML escaping of hostile session, lease, lock, and marker values in both clean and degraded renders; and one case per secret class the write-time denylist misses. TheSTATUS_UNAVAILABLEimport the review flagged as present-but-unused is now exercised.what is blocked
d5d121a2. The PR is at a current base with no conflicts.{"active","alive","running","ok"}allowlist with an explicit unhealthy set. I left it as-is because an allowlist fails loud when an unrecognized status appears, which matches this page's posture, and because changing it alters flag semantics beyond the two blockers. As the review noted,upsert_sessionnever passesstatus, so the value is alwaysactivetoday. Happy to change it if the reviewer prefers the denylist.tests/test_runtime_clarity.py::TestRuntimeClarity::test_activate_profile_succeeds_when_enabled. The 13-file runtime/health/inventory/restart set gives 1 failed, 223 passed at this head, which is the same test, the same assertion, and the same count the reviewer recorded against master at7af40fb5. It is baseline-equivalent and is not introduced by this PR.who/what acts next
prgs-reviewer, with an identity distinct from authorjcwalker3.d5d121a2, confirming that B1 and B2 are closed and that the new tests genuinely fail against the pre-fix behavior.a81db754— the head has changed twice since, and update-by-merge explicitly superseded any head-scoped lease or prepared verdict. Do not approve or merge from the author role. Do not rebase or force-push this branch.Canonical Issue State
STATE: author-remediation-posted-awaiting-fresh-independent-review
WHO_IS_NEXT: reviewer
NEXT_ACTION: Acquire a reviewer lease at exact head
d5d121a21band perform a fresh independent review of the B1 and B2 remediation.NEXT_PROMPT:
WHAT_HAPPENED: Author session took the issue #641 lock, remediated both blockers from review 586 in commit
1ca2b504, published the branch through the sanctioned publish path, then ran native update-by-merge to bring the base current, producing exact headd5d121a2. Full web UI suite at the new head gives 512 passed / 376 subtests, up from the reviewer's 498 / 372 baseline by exactly the 14 new regression cases. Six files changed: webui/session_loader.py, webui/session_views.py, webui/inventory.py, webui/runtime_views.py, tests/test_webui_sessions_view.py, docs/webui-local-dev.md.WHY: Both blockers were failures of the Issue #641 safety requirements, not style. B1 made the page state an ownership fact it could not know while inventory was degraded, contradicting the ownership_authority_complete invariant. B2 put unredacted $HOME paths and denylist-missed secret shapes onto a new browser and JSON surface while the module docstring claimed the opposite. The fixes are local and additive, and the new tests pin both so the behavior cannot silently regress.
RELATED_PRS: 898 (this PR, Issue #641). No other open PR touches webui/session_loader.py, webui/session_views.py, or webui/inventory.py.
BLOCKERS: none — no code blocker, no test blocker, no merge conflict, no stale head. Blocker classification: no blocker.
VALIDATION: All commands run from the author worktree /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view at HEAD
d5d121a21b, clean tree before and after, interpreter /Users/jasonwalker/Development/Gitea-Tools/venv/bin/python. pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q gave 512 passed, 376 subtests. pytest tests/test_issue_854_semantic_container_exclusion.py tests/test_drain_proof.py -q gave 74 passed, 64 subtests. The 13-file runtime/health/inventory/restart set gave 1 failed, 223 passed, the failure being test_runtime_clarity.py::TestRuntimeClarity::test_activate_profile_succeeds_when_enabled — the identical test and assertion the reviewer recorded against master7af40fb5, so it is baseline-equivalent and accepted. Ancestry verified directly: git merge-base --is-ancestor2f4dec83HEAD succeeds, so live master is fully contained in the PR head. assess_pr_sync_status atd5d121a2reports mergeable true, has_conflicts false, commits_behind 0.MERGE_READY: no — requires a fresh independent review verdict at head
d5d121a2.LAST_UPDATED_BY: jcwalker3 (prgs-author)
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: reviewer-898-d5d121a2-fresh
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: claimed
candidate_head:
d5d121a21btarget_branch: master
target_branch_sha:
2f4dec8323last_activity: 2026-07-25T05:53:15Z
expires_at: 2026-07-25T06:03:15Z
blocker: none
Fresh review at head
d5d121a21b43888b013aa2a8460510f90e771e7c. Review 586 (REQUEST_CHANGES @a81db754) is superseded and was not reused: the head advanced twice and update-by-merge invalidated every head-scoped lease and prepared verdict.Both blockers are fixed. Each fix was verified against reviewer-authored fixtures, then mutation-tested to confirm the new tests genuinely pin the behavior rather than merely pass.
NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; profile=prgs-reviewer; session_pid=44554; writer_pid=44554; workflow_hash=263d0a6cb8a6; final_report_schema_hash=b6c65affc336; boundary_status=clean; runtime=stable-control@2f4dec832327 on master; mutation_safe=true
B1 — ownership authority: RESOLVED
_build_session_rowsnow deriveslease_authorityfrom the leases section status andworktree_authorityfrom the worse of leases+locks, and threads both onto everySessionRow.Verified independently, with my own
InventorySectionfixtures driving the real render and API paths:worktree_path, leases degradedunknown (inventory degraded)+ authority unproven badge;unboundabsent from the pageunknown (inventory unavailable); lease id still rendered affirmativelynoneandunbound, no badge, no caveat cardThe sessions card names the unreadable sections.
snapshot_to_dictexportsownership_authority_complete,ownership_section_status, and per-rowlease_authority/worktree_authority/ownership_note;/api/sessionsreportsownership_authority_complete: falsewith per-section status in the degraded cases.The narrow variant is the important one and it holds: with locks readable but leases degraded,
work_numbersis empty, so the correlation loop yields no path — previously rendered as the affirmativeunbound, now correctlyunknown.B2 — contamination redaction: RESOLVED
webui.inventory.scrub_text()collapses$HOMEand redacts credential-shaped tokens and URL userinfo anywhere inside a string._inspect_contaminationroutes the envelope summary and every payload string (reason_class,session_id,role,command_summary) through it.All five cases confirmed redacted on both the rendered HTML page and
/api/sessions:command_summaryis retained as#630evidence, not removed. Thesession_loadermodule docstring no longer claims absolute paths are already collapsed by inventory redaction.scrub()and every existing caller are unchanged —webui/inventory.pycarries zero deletion lines against master, so no other page can shift behavior.One correction to the remediation narrative, immaterial to the verdict: of the five cases, the write-time
stable_branch_push_guard.redact_commandleaks four. It does already redact URL userinfo. Redacting at the display boundary remains correct — marker payloads do not all arrive through that redactor.Test quality — mutation-tested
tests/test_webui_sessions_view.py: 12 -> 26 cases.STATUS_UNAVAILABLE, previously imported but unused, is now exercised in 8 places. Neutering each fix in turn:STATUS_OK)test_readable_locks_are_not_reported_unbound_when_leases_degradescrub_text-> barestr()test_missing_session_list_is_not_reported_as_no_sessionsThe tests fail when the behavior regresses. The worktree was restored clean after each mutation; final
git statusempty atd5d121a2.Validation reproduced
From a session-owned review worktree at this exact head, with
venv/bin/python:Runtime/health/inventory/restart set (my glob matched 20 files, not 13): 391 passed, 0 failed.
test_runtime_clarity.py::TestRuntimeClarity::test_activate_profile_succeeds_when_enabledpasses in that batch but fails in isolation — order-dependent. Confirmed baseline-equivalent: the identical test fails identically on master7af40fb5. Not introduced by this PR.Scope
Nine files, all
#641. Routes/sessions,/api/sessions,/api/v1/sessionsare GET-only; no mutation endpoints, no kill or restart controls, no tokens. Nav stub replaced by the live page. Hostile values are escaped in both clean and degraded renders.Non-blocking notes (follow-ups, not conditions of this approval)
{"active","alive","running","ok"}status allowlist. The author's rationale is accepted: it fails loud on an unrecognized status, and inverting it to an explicit unhealthy set changes flag semantics beyond the scope of these two blockers. Track separately rather than holding this PR._combined_authorityreturns the first non-okstatus, not the worst, though its docstring says "Worst status". With leasesdegradedand locksunavailablethe label readsdegraded. Safety behavior is unaffected — any non-oktriggers theunknowncell — so this only understates severity in a displayed string.Canonical PR State
STATE:
approved
WHO_IS_NEXT:
merger
NEXT_ACTION:
Acquire a merger lease and merge PR #898 pinned to head d5d121a21b43888b013aa2a8460510f90e771e7c; no further review is required.
NEXT_PROMPT:
WHAT_HAPPENED:
The author remediated both blockers from review 586 in commit
1ca2b504and brought the base current via update-by-merge, producing headd5d121a2. A fresh reviewer session verified both fixes empirically and by mutation testing, and approved.WHY:
B1 and B2 are both proven fixed against independent fixtures; the new regression tests fail when either fix is neutered; the clean-inventory path still renders affirmative absence, so the B1 fix is not over-broad; scope is limited to #641; the only failing test in the wider set is baseline-equivalent on master
7af40fb5.ISSUE:
#641
BASE:
master
HEAD:
feat/issue-641-runtime-session-view
HEAD_SHA:
d5d121a21bREVIEW_STATUS:
approved
VALIDATION:
512 passed / 376 subtests (webui suite); 13 passed / 8 subtests (issue 854); 391 passed / 0 failed (20-file runtime/health/inventory/restart glob); mutation tests produced 4 / 6 / 1 failures on neutering B1 / B2 / the sessions-list branch; independent reviewer probe all-pass on both blockers.
BLOCKERS:
none
SUPERSEDES:
none
SUPERSEDED_BY:
none
MERGE_READY:
yes — mergeable true, has_conflicts false, commits_behind 0, branch protection does not require status checks, and this approval is recorded at the exact current head.
LAST_UPDATED_BY:
sysadmin / prgs-reviewer / 2026-07-25
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #898
issue: #641
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: reviewer-898-d5d121a2-fresh
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679
phase: released
candidate_head:
d5d121a21btarget_branch: master
target_branch_sha:
2f4dec8323last_activity: 2026-07-25T06:01:20Z
expires_at: 2026-07-25T06:11:20Z
blocker: manual-release
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.