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
Owner

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 /sessions page 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/sessions
  • webui/nav.py — sessions live (not stub)
  • webui/runtime_views.py — link to /sessions
  • webui/system_health_views.py — recovery card update
  • docs/webui-local-dev.md — document surface

Validation

../../venv/bin/python -m pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q
# focused 12 passed; full webui suite 498 passed

Risk

Low. Read-only UI. No mutation endpoints. No tokens. No kill controls.

Worktree / branch / head

  • Worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view
  • Branch: feat/issue-641-runtime-session-view
  • Commit: 619f67907726631a74e6333d8004a435130e2309
  • Base: master @ a4c73766f4b0cc32f7c3808688eceeb6fee74335

LLM Handoff Metadata

  • LLM-Agent-SHA: llm-641sess20260725
  • LLM-Role: implementer
  • Authenticated-Gitea-User: jcwalker3
  • MCP-Profile: prgs-author
  • Branch: feat/issue-641-runtime-session-view
  • Worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view
  • Self-review allowed: no

No review/merge in this author session.

## 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 `/sessions` page 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/sessions` - `webui/nav.py` — sessions live (not stub) - `webui/runtime_views.py` — link to `/sessions` - `webui/system_health_views.py` — recovery card update - `docs/webui-local-dev.md` — document surface ## Validation ```text ../../venv/bin/python -m pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q # focused 12 passed; full webui suite 498 passed ``` ## Risk Low. Read-only UI. No mutation endpoints. No tokens. No kill controls. ## Worktree / branch / head - Worktree: `/Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view` - Branch: `feat/issue-641-runtime-session-view` - Commit: `619f67907726631a74e6333d8004a435130e2309` - Base: `master` @ `a4c73766f4b0cc32f7c3808688eceeb6fee74335` ## LLM Handoff Metadata - LLM-Agent-SHA: llm-641sess20260725 - LLM-Role: implementer - Authenticated-Gitea-User: jcwalker3 - MCP-Profile: prgs-author - Branch: feat/issue-641-runtime-session-view - Worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view - Self-review allowed: no No review/merge in this author session.
jcwalker3 added 1 commit 2026-07-24 21:47:04 -05:00
Compose runtime health with inventory sessions/namespaces/worktrees into a
live /sessions page and JSON API. Surface stale PID/lease flags and durable
contamination markers when detectable. Recovery links name sanctioned
reconnect/restart paths only — no kill controls.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Owner

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: 619f679077
target_branch: master
target_branch_sha: none
last_activity: 2026-07-25T03:32:57Z
expires_at: 2026-07-25T03:42:57Z
blocker: none

<!-- mcp-review-lease:v1 --> 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: 619f67907726631a74e6333d8004a435130e2309 target_branch: master target_branch_sha: none last_activity: 2026-07-25T03:32:57Z expires_at: 2026-07-25T03:42:57Z blocker: none
jcwalker3 added 1 commit 2026-07-24 22:34:42 -05:00
Owner

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: 619f679077
target_branch: master
target_branch_sha: none
last_activity: 2026-07-25T03:35:03Z
expires_at: 2026-07-25T03:45:03Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> 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: 619f67907726631a74e6333d8004a435130e2309 target_branch: master target_branch_sha: none last_activity: 2026-07-25T03:35:03Z expires_at: 2026-07-25T03:45:03Z blocker: manual-release
Author
Owner

[THREAD STATE LEDGER] PR #898 / Issue #641 author handoff refreshed — re-pinned to head a81db75402

What is true now:

  • Server-side decision state: no server-side decision state changed by this author session. gitea_get_pr_review_feedback for PR #898 returns reviews: [], 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.
  • Local verdict/state: author-side validation complete at head a81db754024f2339a25e1d502a92ebfa506abe5a. No code change was required and none was made. No commit was created and the branch was not moved.
  • Latest known validation: full web UI suite 498 passed, 372 subtests passed; focused sessions-view suite 12 passed; incorporated-master allocator suite 13 passed, 8 subtests passed. All at head a81db754024f2339a25e1d502a92ebfa506abe5a.

What changed:

  • The PR head advanced from the superseded commit 619f67907726631a74e6333d8004a435130e2309 to a81db754024f2339a25e1d502a92ebfa506abe5a.
  • a81db754024f2339a25e1d502a92ebfa506abe5a is a two-parent merge commit. Parent 1 is 619f67907726631a74e6333d8004a435130e2309 (the prior feature tip). Parent 2 is 7af40fb5ff7debd5e9165fe97d9c7c279358e175 (current master). Subject: Merge branch 'master' into feat/issue-641-runtime-session-view. Author jcwalker3, 2026-07-24T22:34:40-05:00.
  • The complete delta 619f6790..a81db754 is exactly two files, both incorporated from master, neither part of this PR's own surface: allocator_service.py (+98/-17) and tests/test_issue_854_semantic_container_exclusion.py (+378, new file). Both originate in PR #883 / issue #854 (allocator vision/roadmap/umbrella container exclusion).
  • Zero webui files, zero docs files, and zero of this PR's own test files were touched by the head movement. No conflict-resolution edits were carried in the merge commit; a scan for conflict markers across webui/, tests/, and allocator_service.py at the new head returns nothing.
  • No functional coupling exists between the incorporated master change and the runtime/session view: allocator_service is not imported or referenced by webui/session_loader.py, webui/session_views.py, webui/nav.py, webui/runtime_views.py, or webui/system_health_views.py.
  • The author worktree was fast-forwarded from 619f6790 to the already-published a81db754. That was a local checkout advance only against prgs/feat/issue-641-runtime-session-view; no push, no commit, no branch move, no Gitea mutation.

What is blocked:

  • Blocker classification: no blocker
  • The PR carries no unresolved conflicts. gitea_assess_pr_sync_status for PR #898 reports mergeable=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).
  • One control-plane hygiene note, not a gate on review: the author lease for issue #641 (lease-5e7ef35f96c64c3e, session prgs-author-64404-1d04c608, expected_head_sha=a4c73766f4b0cc32f7c3808688eceeb6fee74335) carries status: active with freshness: stale_dead_process and owner_pid_alive: false. active_locks_and_leases.author_lock is false. 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:

  • Next actor: reviewer
  • Required action: open a fresh independent gitea-reviewer session pinned to head a81db754024f2339a25e1d502a92ebfa506abe5a and perform a first formal review of PR #898 at exactly that head.
  • Do not do: do not review, cite, or reason from the superseded 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 lease lease-5e7ef35f96c64c3e.

Author handoff evidence — PR #898 @ a81db75402

Pinned state

Field Value
PR #898
PR state open
Base branch master
Head branch feat/issue-641-runtime-session-view
Live PR head a81db754024f2339a25e1d502a92ebfa506abe5a
Superseded head 619f67907726631a74e6333d8004a435130e2309
Current master 7af40fb5ff7debd5e9165fe97d9c7c279358e175
Verified worktree SHA a81db754024f2339a25e1d502a92ebfa506abe5a (clean; git status --porcelain empty)
Worktree branches/feat-issue-641-runtime-session-view
Closes #641 in PR body present
Mergeability mergeable=true, has_conflicts=false, commits_behind=0
Prior reviewer verdict none recorded (reviews: [])

git merge-base 7af40fb5 a81db754 = 7af40fb5ff7debd5e9165fe97d9c7c279358e175, so the branch contains current master in full and the three-dot diff is the exact effective PR surface.

Preflight gates at time of this handoff

gitea_whoami (remote prgs): profile prgs-author, role: author, identity jcwalker3, identity_match: true.

gitea_assess_master_parity (remote prgs):

in_parity=true
mutation_safe=true
restart_required=false
live_stale=false
stale=false
startup_head = current_head = local_head = live_remote_head = 7af40fb5ff7debd5e9165fe97d9c7c279358e175

gitea_resolve_task_capability for the comment mutation returned stop_required=false and restart_required=false.

Effective PR diff against current master

git diff --stat 7af40fb5...a81db754 — 8 files, +1280 / -15:

 docs/webui-local-dev.md           |  29 ++-
 tests/test_webui_sessions_view.py | 457 ++++++++++++++++++++++++++++++++++++++
 webui/app.py                      |  20 ++
 webui/nav.py                      |   7 +-
 webui/runtime_views.py            |   4 +-
 webui/session_loader.py           | 428 +++++++++++++++++++++++++++++++++++
 webui/session_views.py            | 344 ++++++++++++++++++++++++++++
 webui/system_health_views.py      |   6 +-

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

  1. Routes. webui/app.py registers Route("/sessions", sessions, methods=["GET"]), Route("/api/sessions", api_sessions, methods=["GET"]), and Route("/api/v1/sessions", api_sessions, methods=["GET"]). All three are GET-only; the versioned path is a direct alias of the same handler.
  2. Runtime-health and session/namespace/worktree composition. webui/session_loader.load_session_view_snapshot composes webui.runtime_health.load_runtime_snapshot (#430) with webui.inventory.load_inventory_snapshot (#636) and correlates leases to sessions by session_id, and worktree bindings to sessions by matching lock issue_number against lease work_number. webui/session_views.py renders the runtime banner, the sessions table, a namespaces/capabilities section, and a worktree-bindings section.
  3. Dead-PID, expired-lease, and contamination indicators. _build_session_rows emits pid-dead when pid_alive is False, status:<value> for any non-live status, and lease-expired / active-lease-past-expiry from lease expiry state. Contamination is inspected for KIND_RUNTIME_RECOVERY_CONTAMINATION and KIND_STABLE_BRANCH_CONTAMINATION; a marker with no session binding is surfaced against every session as <kind>:process-wide so it cannot be silent (#630).
  4. Safe handling of missing, malformed, partial, or stale inventory. load_session_view_snapshot catches runtime-loader failure and substitutes a placeholder RuntimeSnapshot carrying the error, catches inventory-loader failure and falls back to an empty snapshot, and accumulates both into fetch_error, which the page renders as an explicit "Partial load" banner. _inspect_contamination never raises into the render path. Row construction skips records without a session_id and coerces pid / pid_alive by type check. Section rendering handles section 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.
  5. Sanctioned recovery guidance only. SANCTIONED_RECOVERY_DOCS is 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 manual pkill / kill of MCP daemons is contamination (#630), not recovery. There are no restart, kill, or takeover controls and no mutating endpoints.
  6. Disclosure. No token, credential, or authorization material is read or emitted. The JSON export carries read_only: true, phase: 1, mutations: []. Filesystem exposure is limited to worktree bindings, which is the substance of acceptance criterion 2; the worktrees section prefers rel_path over the absolute path.
  7. Escaping, navigation, documentation, route integration. Every interpolated dynamic value in webui/session_views.py passes through html.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 / _flags helpers. webui/nav.py removes /sessions from STUB_PAGES and drops the "stub" marker from the nav item. webui/runtime_views.py and webui/system_health_views.py link to /sessions. docs/webui-local-dev.md documents all three routes and moves /sessions out 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 a81db75402

Run from branches/feat-issue-641-runtime-session-view with ../../venv/bin/python (Python 3.14.5, pytest 9.1.1).

$ ../../venv/bin/python -m pytest tests/test_webui_sessions_view.py -v
12 passed in 0.71s

$ ../../venv/bin/python -m pytest tests/test_webui_runtime_health.py tests/test_webui_system_health.py \
    tests/test_webui_system_health_dashboard.py tests/test_webui_shell.py tests/test_webui_skeleton.py \
    tests/test_webui_inventory.py tests/test_webui_sanctioned_restart.py -q
158 passed, 84 subtests passed in 1.67s

$ ../../venv/bin/python -m pytest tests/test_webui_*.py -q
498 passed, 372 subtests passed in 3.27s

$ ../../venv/bin/python -m pytest tests/test_issue_854_semantic_container_exclusion.py -q
13 passed, 8 subtests passed in 0.73s

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), and TestSessionsRoutes (/api/sessions JSON, nav marks sessions live, /sessions page live).

The worktree is clean at a81db754024f2339a25e1d502a92ebfa506abe5a after 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 a81db75402 and perform the first formal review of this PR at exactly that head.

NEXT_PROMPT:

Invoke the canonical `gitea-workflow` skill first.

Perform a first formal review of PR #898 in `prgs / Scaled-Tech-Consulting / Gitea-Tools`.

Pinned candidate:
- PR: #898
- Issue: #641
- Branch: feat/issue-641-runtime-session-view
- Head to review: a81db754024f2339a25e1d502a92ebfa506abe5a
- Base: master @ 7af40fb5ff7debd5e9165fe97d9c7c279358e175
- Superseded head, do NOT review: 619f67907726631a74e6333d8004a435130e2309

No prior review verdict exists on this PR. `gitea_get_pr_review_feedback` returns
`reviews: []`. Do not carry any approval forward from any head.

Use only the sanctioned `gitea-reviewer` MCP namespace. Do not perform author,
merger, controller, or reconciler operations. Do not use direct Gitea API or
unsupported CLI fallbacks.

Before mutation, verify profile `prgs-reviewer`, role reviewer, and require
`in_parity=true`, `mutation_safe=true`, `restart_required=false`,
`live_stale=false`, `stop_required=false`. Re-pin the live PR head and stop if it
is no longer a81db754024f2339a25e1d502a92ebfa506abe5a.

Review the effective diff `7af40fb5...a81db754` (8 files) against Issue #641:
/sessions, /api/sessions, /api/v1/sessions; runtime-health plus
session/namespace/worktree composition; dead-PID, expired-lease and contamination
indicators; safe handling of missing, malformed, partial, or stale inventory;
sanctioned recovery guidance only; no secret, token, or unnecessary filesystem
disclosure; escaping, navigation, documentation, and route integration.

Reviewer judgment is specifically requested on one point the author flagged: the
contamination table renders `command_summary` from the durable marker payload,
which can contain the recorded contaminating command line. Confirm whether that
disclosure is intended by #630 or should be narrowed.

Record the verdict at exactly a81db754024f2339a25e1d502a92ebfa506abe5a and stop at
the reviewer boundary. Do not merge.

WHAT_HAPPENED:
The PR head advanced from 619f679077 to a81db75402 via 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 7af40fb5ff before 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)

[THREAD STATE LEDGER] PR #898 / Issue #641 author handoff refreshed — re-pinned to head a81db754024f2339a25e1d502a92ebfa506abe5a What is true now: - Server-side decision state: no server-side decision state changed by this author session. `gitea_get_pr_review_feedback` for PR #898 returns `reviews: []`, `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. - Local verdict/state: author-side validation complete at head `a81db754024f2339a25e1d502a92ebfa506abe5a`. No code change was required and none was made. No commit was created and the branch was not moved. - Latest known validation: full web UI suite `498 passed, 372 subtests passed`; focused sessions-view suite `12 passed`; incorporated-master allocator suite `13 passed, 8 subtests passed`. All at head `a81db754024f2339a25e1d502a92ebfa506abe5a`. What changed: - The PR head advanced from the superseded commit `619f67907726631a74e6333d8004a435130e2309` to `a81db754024f2339a25e1d502a92ebfa506abe5a`. - `a81db754024f2339a25e1d502a92ebfa506abe5a` is a two-parent merge commit. Parent 1 is `619f67907726631a74e6333d8004a435130e2309` (the prior feature tip). Parent 2 is `7af40fb5ff7debd5e9165fe97d9c7c279358e175` (current `master`). Subject: `Merge branch 'master' into feat/issue-641-runtime-session-view`. Author `jcwalker3`, `2026-07-24T22:34:40-05:00`. - The complete delta `619f6790..a81db754` is exactly two files, both incorporated from `master`, neither part of this PR's own surface: `allocator_service.py` (+98/-17) and `tests/test_issue_854_semantic_container_exclusion.py` (+378, new file). Both originate in PR #883 / issue #854 (allocator vision/roadmap/umbrella container exclusion). - Zero webui files, zero docs files, and zero of this PR's own test files were touched by the head movement. No conflict-resolution edits were carried in the merge commit; a scan for conflict markers across `webui/`, `tests/`, and `allocator_service.py` at the new head returns nothing. - No functional coupling exists between the incorporated `master` change and the runtime/session view: `allocator_service` is not imported or referenced by `webui/session_loader.py`, `webui/session_views.py`, `webui/nav.py`, `webui/runtime_views.py`, or `webui/system_health_views.py`. - The author worktree was fast-forwarded from `619f6790` to the already-published `a81db754`. That was a local checkout advance only against `prgs/feat/issue-641-runtime-session-view`; no push, no commit, no branch move, no Gitea mutation. What is blocked: - Blocker classification: no blocker - The PR carries no unresolved conflicts. `gitea_assess_pr_sync_status` for PR #898 reports `mergeable=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). - One control-plane hygiene note, not a gate on review: the author lease for issue #641 (`lease-5e7ef35f96c64c3e`, session `prgs-author-64404-1d04c608`, `expected_head_sha=a4c73766f4b0cc32f7c3808688eceeb6fee74335`) carries `status: active` with `freshness: stale_dead_process` and `owner_pid_alive: false`. `active_locks_and_leases.author_lock` is `false`. 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: - Next actor: reviewer - Required action: open a fresh independent `gitea-reviewer` session pinned to head `a81db754024f2339a25e1d502a92ebfa506abe5a` and perform a first formal review of PR #898 at exactly that head. - Do not do: do not review, cite, or reason from the superseded 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 lease `lease-5e7ef35f96c64c3e`. --- ## Author handoff evidence — PR #898 @ a81db754024f2339a25e1d502a92ebfa506abe5a ### Pinned state | Field | Value | |---|---| | PR | #898 | | PR state | open | | Base branch | `master` | | Head branch | `feat/issue-641-runtime-session-view` | | Live PR head | `a81db754024f2339a25e1d502a92ebfa506abe5a` | | Superseded head | `619f67907726631a74e6333d8004a435130e2309` | | Current `master` | `7af40fb5ff7debd5e9165fe97d9c7c279358e175` | | Verified worktree SHA | `a81db754024f2339a25e1d502a92ebfa506abe5a` (clean; `git status --porcelain` empty) | | Worktree | `branches/feat-issue-641-runtime-session-view` | | `Closes #641` in PR body | present | | Mergeability | `mergeable=true`, `has_conflicts=false`, `commits_behind=0` | | Prior reviewer verdict | none recorded (`reviews: []`) | `git merge-base 7af40fb5 a81db754` = `7af40fb5ff7debd5e9165fe97d9c7c279358e175`, so the branch contains current `master` in full and the three-dot diff is the exact effective PR surface. ### Preflight gates at time of this handoff `gitea_whoami` (remote `prgs`): profile `prgs-author`, `role: author`, identity `jcwalker3`, `identity_match: true`. `gitea_assess_master_parity` (remote `prgs`): ```text in_parity=true mutation_safe=true restart_required=false live_stale=false stale=false startup_head = current_head = local_head = live_remote_head = 7af40fb5ff7debd5e9165fe97d9c7c279358e175 ``` `gitea_resolve_task_capability` for the comment mutation returned `stop_required=false` and `restart_required=false`. ### Effective PR diff against current master `git diff --stat 7af40fb5...a81db754` — 8 files, +1280 / -15: ```text docs/webui-local-dev.md | 29 ++- tests/test_webui_sessions_view.py | 457 ++++++++++++++++++++++++++++++++++++++ webui/app.py | 20 ++ webui/nav.py | 7 +- webui/runtime_views.py | 4 +- webui/session_loader.py | 428 +++++++++++++++++++++++++++++++++++ webui/session_views.py | 344 ++++++++++++++++++++++++++++ webui/system_health_views.py | 6 +- ``` 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 1. **Routes.** `webui/app.py` registers `Route("/sessions", sessions, methods=["GET"])`, `Route("/api/sessions", api_sessions, methods=["GET"])`, and `Route("/api/v1/sessions", api_sessions, methods=["GET"])`. All three are GET-only; the versioned path is a direct alias of the same handler. 2. **Runtime-health and session/namespace/worktree composition.** `webui/session_loader.load_session_view_snapshot` composes `webui.runtime_health.load_runtime_snapshot` (#430) with `webui.inventory.load_inventory_snapshot` (#636) and correlates leases to sessions by `session_id`, and worktree bindings to sessions by matching lock `issue_number` against lease `work_number`. `webui/session_views.py` renders the runtime banner, the sessions table, a namespaces/capabilities section, and a worktree-bindings section. 3. **Dead-PID, expired-lease, and contamination indicators.** `_build_session_rows` emits `pid-dead` when `pid_alive is False`, `status:<value>` for any non-live status, and `lease-expired` / `active-lease-past-expiry` from lease expiry state. Contamination is inspected for `KIND_RUNTIME_RECOVERY_CONTAMINATION` and `KIND_STABLE_BRANCH_CONTAMINATION`; a marker with no session binding is surfaced against every session as `<kind>:process-wide` so it cannot be silent (#630). 4. **Safe handling of missing, malformed, partial, or stale inventory.** `load_session_view_snapshot` catches runtime-loader failure and substitutes a placeholder `RuntimeSnapshot` carrying the error, catches inventory-loader failure and falls back to an empty snapshot, and accumulates both into `fetch_error`, which the page renders as an explicit "Partial load" banner. `_inspect_contamination` never raises into the render path. Row construction skips records without a `session_id` and coerces `pid` / `pid_alive` by type check. Section rendering handles `section 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. 5. **Sanctioned recovery guidance only.** `SANCTIONED_RECOVERY_DOCS` is 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 manual `pkill` / `kill` of MCP daemons is contamination (#630), not recovery. There are no restart, kill, or takeover controls and no mutating endpoints. 6. **Disclosure.** No token, credential, or authorization material is read or emitted. The JSON export carries `read_only: true`, `phase: 1`, `mutations: []`. Filesystem exposure is limited to worktree bindings, which is the substance of acceptance criterion 2; the worktrees section prefers `rel_path` over the absolute `path`. 7. **Escaping, navigation, documentation, route integration.** Every interpolated dynamic value in `webui/session_views.py` passes through `html.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` / `_flags` helpers. `webui/nav.py` removes `/sessions` from `STUB_PAGES` and drops the `"stub"` marker from the nav item. `webui/runtime_views.py` and `webui/system_health_views.py` link to `/sessions`. `docs/webui-local-dev.md` documents all three routes and moves `/sessions` out 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 a81db754024f2339a25e1d502a92ebfa506abe5a Run from `branches/feat-issue-641-runtime-session-view` with `../../venv/bin/python` (Python 3.14.5, pytest 9.1.1). ```text $ ../../venv/bin/python -m pytest tests/test_webui_sessions_view.py -v 12 passed in 0.71s $ ../../venv/bin/python -m pytest tests/test_webui_runtime_health.py tests/test_webui_system_health.py \ tests/test_webui_system_health_dashboard.py tests/test_webui_shell.py tests/test_webui_skeleton.py \ tests/test_webui_inventory.py tests/test_webui_sanctioned_restart.py -q 158 passed, 84 subtests passed in 1.67s $ ../../venv/bin/python -m pytest tests/test_webui_*.py -q 498 passed, 372 subtests passed in 3.27s $ ../../venv/bin/python -m pytest tests/test_issue_854_semantic_container_exclusion.py -q 13 passed, 8 subtests passed in 0.73s ``` 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), and `TestSessionsRoutes` (`/api/sessions` JSON, nav marks sessions live, `/sessions` page live). The worktree is clean at `a81db754024f2339a25e1d502a92ebfa506abe5a` after 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 a81db754024f2339a25e1d502a92ebfa506abe5a and perform the first formal review of this PR at exactly that head. NEXT_PROMPT: ```text Invoke the canonical `gitea-workflow` skill first. Perform a first formal review of PR #898 in `prgs / Scaled-Tech-Consulting / Gitea-Tools`. Pinned candidate: - PR: #898 - Issue: #641 - Branch: feat/issue-641-runtime-session-view - Head to review: a81db754024f2339a25e1d502a92ebfa506abe5a - Base: master @ 7af40fb5ff7debd5e9165fe97d9c7c279358e175 - Superseded head, do NOT review: 619f67907726631a74e6333d8004a435130e2309 No prior review verdict exists on this PR. `gitea_get_pr_review_feedback` returns `reviews: []`. Do not carry any approval forward from any head. Use only the sanctioned `gitea-reviewer` MCP namespace. Do not perform author, merger, controller, or reconciler operations. Do not use direct Gitea API or unsupported CLI fallbacks. Before mutation, verify profile `prgs-reviewer`, role reviewer, and require `in_parity=true`, `mutation_safe=true`, `restart_required=false`, `live_stale=false`, `stop_required=false`. Re-pin the live PR head and stop if it is no longer a81db754024f2339a25e1d502a92ebfa506abe5a. Review the effective diff `7af40fb5...a81db754` (8 files) against Issue #641: /sessions, /api/sessions, /api/v1/sessions; runtime-health plus session/namespace/worktree composition; dead-PID, expired-lease and contamination indicators; safe handling of missing, malformed, partial, or stale inventory; sanctioned recovery guidance only; no secret, token, or unnecessary filesystem disclosure; escaping, navigation, documentation, and route integration. Reviewer judgment is specifically requested on one point the author flagged: the contamination table renders `command_summary` from the durable marker payload, which can contain the recorded contaminating command line. Confirm whether that disclosure is intended by #630 or should be narrowed. Record the verdict at exactly a81db754024f2339a25e1d502a92ebfa506abe5a and stop at the reviewer boundary. Do not merge. ``` WHAT_HAPPENED: The PR head advanced from 619f67907726631a74e6333d8004a435130e2309 to a81db754024f2339a25e1d502a92ebfa506abe5a via a two-parent merge commit that brought current master (7af40fb5ff7debd5e9165fe97d9c7c279358e175) 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 7af40fb5ff7debd5e9165fe97d9c7c279358e175 before 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 a4c73766f4b0cc32f7c3808688eceeb6fee74335) 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 a81db754024f2339a25e1d502a92ebfa506abe5a, 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)
Owner

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: a81db75402
target_branch: master
target_branch_sha: 7af40fb5ff
last_activity: 2026-07-25T04:21:06Z
expires_at: 2026-07-25T04:31:06Z
blocker: none

<!-- mcp-review-lease:v1 --> 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: a81db754024f2339a25e1d502a92ebfa506abe5a target_branch: master target_branch_sha: 7af40fb5ff7debd5e9165fe97d9c7c279358e175 last_activity: 2026-07-25T04:21:06Z expires_at: 2026-07-25T04:31:06Z blocker: none
Owner

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: a81db75402
target_branch: master
target_branch_sha: 7af40fb5ff
last_activity: 2026-07-25T04:32:33Z
expires_at: 2026-07-25T04:42:33Z
blocker: none

<!-- mcp-review-lease:v1 --> 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: a81db754024f2339a25e1d502a92ebfa506abe5a target_branch: master target_branch_sha: 7af40fb5ff7debd5e9165fe97d9c7c279358e175 last_activity: 2026-07-25T04:32:33Z expires_at: 2026-07-25T04:42:33Z blocker: none
sysadmin requested changes 2026-07-24 23:34:21 -05:00
Dismissed
sysadmin left a comment
Owner

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:

Author task for Scaled-Tech-Consulting/Gitea-Tools, Issue #641, PR #898,
branch feat/issue-641-runtime-session-view, reviewed head
a81db754024f2339a25e1d502a92ebfa506abe5a.

A reviewer submitted REQUEST_CHANGES at that head with two blockers. Work in
the existing worktree
/Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view
under the prgs-author profile. Take the issue lock first. Do not rebase or
force-push.

B1 - Degraded ownership inventory is rendered as affirmative absence.
webui/session_loader.py::_build_session_rows reads the "sessions", "leases",
and "locks" sections without consulting their status. When the sessions
section reads OK but leases and/or locks are degraded or unavailable, every
row still emits lease_ids=() and worktree_paths=(), which
webui/session_views.py::_render_session_row prints as "none" and "unbound".
That asserts "this session holds no lease and has no worktree binding" when
the truth is "the lease store could not be read". This contradicts the
documented invariant on InventorySnapshot.ownership_authority_complete in
webui/inventory.py, which states that while it is false the snapshot must not
describe any work item as unowned. Note that _namespaces_section and
_worktrees_section already check section.ok and render a degraded badge, so
the pattern to follow exists in this same file. Fix: thread ownership-section
status into the row builder and the row renderer, render "unknown (inventory
degraded)" rather than "none"/"unbound" when the backing section is not OK,
and add ownership_authority_complete plus per-row authority state to the
snapshot_to_dict JSON export so /api/sessions consumers can tell the two
cases apart.

B2 - Contamination payload strings bypass the redaction every other field on
the page goes through. webui/session_loader.py::_inspect_contamination copies
command_summary, session_id, role, and reason_class straight out of the
marker payload with only str(). Inventory items reach the same page through
webui.inventory.scrub(), which collapses $HOME to ~ and redacts URL userinfo
and credential-shaped keys. The contamination row does not. The write-time
redactor stable_branch_push_guard.redact_command is a narrow denylist and was
verified in review to leave these intact: absolute $HOME paths,
-H 'X-Api-Key: <value>', --password <value>, and PRIVATE_KEY=<value>. It does
correctly redact GITEA_TOKEN=<value> and https://user:tok@host userinfo. This
PR is the first web surface to render command_summary at all, in both the
HTML table and the JSON API. Fix: pass contamination payload strings through
webui.inventory.scrub() (or at minimum redact_path) in _inspect_contamination
so the page is internally consistent, and correct the module docstring at
webui/session_loader.py line 15, which currently claims absolute paths are
collapsed by inventory redaction. Keep the field itself: it is legitimate
issue #630 evidence. Do not remove it.

Also add regression tests in tests/test_webui_sessions_view.py for the
degraded-ownership render (the file already imports STATUS_UNAVAILABLE and
never uses it) and for HTML escaping of hostile session/marker values.

When done: run
/Users/jasonwalker/Development/Gitea-Tools/venv/bin/python -m pytest
tests/test_webui_sessions_view.py tests/test_webui_*.py -q
from the worktree, publish the branch, and hand back to a reviewer session.

WHAT_HAPPENED: Independent reviewer validation of PR #898 at head a81db75402. Confirmed the head is the two-parent merge of 619f679077 with master 7af40fb5ff, 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: a81db75402
REVIEW_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 master 7af40fb5ff, 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 @ a81db754

Decision: REQUEST_CHANGES. Two blockers, four non-blocking notes.

Reviewer sysadmin / prgs-reviewer; author jcwalker3. 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 from 619f679.

Head and effective diff

a81db754 is the two-parent merge you reported: parents 619f6790 (feature) and 7af40fb5 (master). git merge-base 7af40fb5 a81db754 returns 7af40fb5 exactly, 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.

  • Routing. /sessions, /api/sessions, /api/v1/sessions are all registered GET-only. The nav stub entry is removed cleanly, STUB_PAGES no longer carries /sessions, and exactly one nav item resolves to it.
  • Escaping. Every interpolation in session_views.py passes through html.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.
  • Recovery guidance. SANCTIONED_RECOVERY_DOCS names reconnect and operator-restart documentation only. The section states plainly that manual pkill is contamination, not recovery. No restart, kill, or takeover control exists on the page.
  • Fail-soft composition. The runtime and inventory loaders are individually guarded, and I exercised the fallback at session_loader.py:382 directly: 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.
  • Dead-PID accuracy. pid_alive is False is the only thing that raises pid-dead; None stays neutral and renders as a skipped badge. Healthy records are not mislabelled.
  • PR #883 interaction. The master changes merged in through PR #883 do not touch the web UI. tests/test_issue_854_semantic_container_exclusion.py passes at this head: 13 passed, 8 subtests. No conflict, no regression.
  • Integration consistency. runtime_views.py, system_health_views.py, and docs/webui-local-dev.md are 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_dict and inventory_snapshot_to_dict were already exposed at /api/runtime and /api/v1/inventory on 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 the sessions, leases, and locks sections without ever consulting their status. Those three are exactly OWNERSHIP_SECTIONS in webui/inventory.py:61.

Reproduced directly at this head - sessions section OK, leases and locks unavailable:

ownership_authority_complete: False
inventory.status:             degraded
row lease_ids: ()   worktree_paths: ()
renders "unbound":            True
renders leases "none":        True
per-row degraded/unavailable caveat: False

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.py documents this exact hazard on ownership_authority_complete:

While this is false the snapshot must not describe any work item as unowned: a lease the reader could not load is not an absent lease.

The mitigations present are not sufficient to carry this. The summary bar shows a global Inventory: degraded badge, 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_ids and worktree_paths come 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_section and _worktrees_section both check section.ok and render a degraded badge; the sessions table is the one place that does not.

A narrower, fully realistic variant: when the work_items table is absent the leases section is STATUS_DEGRADED and every lease row carries work_kind/work_number of None. work_numbers stays empty, so the lock-correlation loop can never match, and every session row reads unbound even when locks with real worktree_path values are present and readable.

Fix. Thread ownership-section status into the row builder and renderer; render unknown (inventory degraded) instead of none/unbound when the backing section is not OK; expose ownership_authority_complete and per-row authority state in snapshot_to_dict.

B2 (blocker) - contamination payload skips the redaction every sibling field uses

This is the explicit command_summary judgment.

My judgment: command_summary is necessary evidence for issue #630 and should stay. reason_class alone 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: grep for command_summary under webui/ on master 7af40fb5 returns 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) copies command_summary, session_id, role, and reason_class out of the payload with only str(). Every inventory-sourced field on the same page arrives via webui.inventory.scrub(), which collapses $HOME to ~ 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_command is a narrow denylist, verified by running it at this head:

IN : pkill -f /Users/<user>/Development/Gitea-Tools/gitea_mcp_server.py
OUT: pkill -f /Users/<user>/Development/Gitea-Tools/gitea_mcp_server.py   <- $HOME not collapsed

IN : curl -H 'X-Api-Key: SUPERSECRET123' https://gitea.prgs.cc/api
OUT: curl -H 'X-Api-Key: SUPERSECRET123' https://gitea.prgs.cc/api        <- not redacted

IN : git push --password hunter2 origin master
OUT: git push --password hunter2 origin master                            <- not redacted

IN : PRIVATE_KEY=abc123 python deploy.py
OUT: PRIVATE_KEY=abc123 python deploy.py                                  <- not redacted

IN : GITEA_TOKEN=abc123 git push
OUT: GITEA_TOKEN=*** git push                                             <- correctly redacted

IN : git push https://user:[email protected]/x.git master
OUT: git push https://***@gitea.prgs.cc/x.git master                      <- correctly redacted

It catches KEY=VALUE for 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:15 states "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, with assess_bind_host refusing 0.0.0.0) genuinely limits the audience, and I weighed it. It does not resolve the finding: /api/v1/sessions is 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 minimum redact_path) in _inspect_contamination, and correct the docstring. Keep the field.

Non-blocking notes

  • N1 - dead code. locks_by_session_hint (session_loader.py:241) is populated by a for loop whose body is pass, then discarded via _ = locks_by_session_hint at line 331. Remove both, or implement the correlation the comment describes.
  • N2 - the degraded path was meant to be tested. tests/test_webui_sessions_view.py:26 imports STATUS_UNAVAILABLE and never uses it. That is the coverage gap behind B1. There is also no escaping test and no lease-expired test; I verified both by hand, but they should be pinned.
  • N3 - status allowlist. status not in {"active","alive","running","ok"} raises a status:<value> stale badge for anything else. Harmless today: upsert_session is only called from allocator_service.py:890, which never passes status, so the value is always active. It becomes a mislabelling risk the moment a legitimate terminal status such as completed or released is introduced. Consider an explicit unhealthy-set instead of an allowlist.
  • N4 - webui/runtime_views.py still 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

REPOSITORY: Scaled-Tech-Consulting/Gitea-Tools
ISSUE: 641
PR: 898
WORKFLOW_STATE: needs-author
HEAD_SHA: a81db754024f2339a25e1d502a92ebfa506abe5a
BASE_BRANCH: master
BASE_OR_MERGE_SHA: 7af40fb5ff7debd5e9165fe97d9c7c279358e175
ACTING_ROLE: reviewer
ACTING_IDENTITY: sysadmin (prgs-reviewer)
COMPLETED_ACTIONS: Acquired reviewer lease 2025-803cbd7aed28 at the exact head (comments 16482, re-acquired 16493 after TTL expiry). Verified the head is the two-parent merge of 619f6790 and master 7af40fb5 and that merge-base equals master head. Reviewed all 8 changed files. Ran focused, full web UI, Issue #854, and a 13-file runtime/health/inventory/restart set against the unmodified PR head, plus a clean baseline comparison at master. Exercised escaping, degraded-ownership rendering, the fail-soft inventory fallback, and the write-time command redactor directly. Submitted REQUEST_CHANGES pinned to a81db754024f2339a25e1d502a92ebfa506abe5a.
VALIDATION_EVIDENCE: Review worktree /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679 at a81db754024f2339a25e1d502a92ebfa506abe5a, clean before and after. 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. The 13-file runtime/health set gave 1 failed, 223 passed; baseline worktree /Users/jasonwalker/Development/Gitea-Tools/branches/mergesim-885 at 7af40fb5ff7debd5e9165fe97d9c7c279358e175 gave 1 failed, 223 passed with identical test and assertion, so baseline-equivalent failure accepted. Baseline webui gave 486 passed, 374 subtests; the +12 test and -2 subtest delta is fully explained by /sessions leaving STUB_PAGES, iterated twice in tests/test_webui_shell.py.
MUTATION_LEDGER: Gitea: reviewer lease acquisition comments 16482 and 16493; one REQUEST_CHANGES review at a81db754024f2339a25e1d502a92ebfa506abe5a; reviewer lease release. Local worktree/index: git checkout --detach a81db754024f2339a25e1d502a92ebfa506abe5a in branches/review-pr898-619f679 (from 619f67907726631a74e6333d8004a435130e2309) and git checkout --detach 7af40fb5ff7debd5e9165fe97d9c7c279358e175 in branches/mergesim-885 (from 2adf612c9600e8c341c881b075280ea1ac908a22). No file edits, no git ref mutations, no branch or push mutations, no merge.
BLOCKERS: B1 degraded ownership inventory rendered as affirmative none/unbound with no per-row caveat, contradicting the ownership_authority_complete invariant; B2 contamination payload fields bypass webui.inventory.scrub, newly exposing $HOME paths and denylist-missed secrets on the web UI and JSON API, with a module docstring claiming otherwise.
NEXT_ACTOR: author
NEXT_ACTION: Fix B1 and B2, add the degraded-ownership and escaping regression tests, push, and request a fresh independent review at the new head.
PROHIBITED_ACTIONS: Do not approve or merge this PR from the author role. Do not rebase or force-push the branch. Do not remove command_summary; redact it instead. Do not reuse this verdict at any other head.
NEXT_PROMPT: Author task for Scaled-Tech-Consulting/Gitea-Tools, Issue #641, PR #898, branch feat/issue-641-runtime-session-view, at reviewed head a81db754024f2339a25e1d502a92ebfa506abe5a. A reviewer submitted REQUEST_CHANGES with two blockers. Work under the prgs-author profile in the existing worktree /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view and take the issue lock first. B1: webui/session_loader.py::_build_session_rows ignores the status of the sessions, leases, and locks sections, so when leases or locks are degraded or unavailable every row still renders lease_ids as none and worktree_paths as unbound, asserting absence where the reader simply could not load the data; this contradicts the ownership_authority_complete invariant documented in webui/inventory.py. Follow the pattern already used by _namespaces_section and _worktrees_section, which check section.ok; render unknown (inventory degraded) instead, and add ownership_authority_complete plus per-row authority state to snapshot_to_dict. B2: webui/session_loader.py::_inspect_contamination copies command_summary, session_id, role, and reason_class from the marker payload with only str(), bypassing webui.inventory.scrub which every other field on the page uses; the write-time redactor stable_branch_push_guard.redact_command leaves absolute $HOME paths, -H 'X-Api-Key: value', --password value, and PRIVATE_KEY=value intact. Pass those strings through webui.inventory.scrub or redact_path, and fix the docstring at webui/session_loader.py line 15 which claims absolute paths are collapsed. Keep the command_summary field; it is legitimate issue #630 evidence. Add regression tests in tests/test_webui_sessions_view.py for the degraded-ownership render (the file already imports STATUS_UNAVAILABLE unused) and for HTML escaping of hostile values. Then run /Users/jasonwalker/Development/Gitea-Tools/venv/bin/python -m pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q from that worktree, publish the branch, and hand back to a reviewer session.
WORKFLOW_FAILURE_ISSUES: none
LAST_UPDATED: 2026-07-25T04:33:00Z
## 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: ```text Author task for Scaled-Tech-Consulting/Gitea-Tools, Issue #641, PR #898, branch feat/issue-641-runtime-session-view, reviewed head a81db754024f2339a25e1d502a92ebfa506abe5a. A reviewer submitted REQUEST_CHANGES at that head with two blockers. Work in the existing worktree /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view under the prgs-author profile. Take the issue lock first. Do not rebase or force-push. B1 - Degraded ownership inventory is rendered as affirmative absence. webui/session_loader.py::_build_session_rows reads the "sessions", "leases", and "locks" sections without consulting their status. When the sessions section reads OK but leases and/or locks are degraded or unavailable, every row still emits lease_ids=() and worktree_paths=(), which webui/session_views.py::_render_session_row prints as "none" and "unbound". That asserts "this session holds no lease and has no worktree binding" when the truth is "the lease store could not be read". This contradicts the documented invariant on InventorySnapshot.ownership_authority_complete in webui/inventory.py, which states that while it is false the snapshot must not describe any work item as unowned. Note that _namespaces_section and _worktrees_section already check section.ok and render a degraded badge, so the pattern to follow exists in this same file. Fix: thread ownership-section status into the row builder and the row renderer, render "unknown (inventory degraded)" rather than "none"/"unbound" when the backing section is not OK, and add ownership_authority_complete plus per-row authority state to the snapshot_to_dict JSON export so /api/sessions consumers can tell the two cases apart. B2 - Contamination payload strings bypass the redaction every other field on the page goes through. webui/session_loader.py::_inspect_contamination copies command_summary, session_id, role, and reason_class straight out of the marker payload with only str(). Inventory items reach the same page through webui.inventory.scrub(), which collapses $HOME to ~ and redacts URL userinfo and credential-shaped keys. The contamination row does not. The write-time redactor stable_branch_push_guard.redact_command is a narrow denylist and was verified in review to leave these intact: absolute $HOME paths, -H 'X-Api-Key: <value>', --password <value>, and PRIVATE_KEY=<value>. It does correctly redact GITEA_TOKEN=<value> and https://user:tok@host userinfo. This PR is the first web surface to render command_summary at all, in both the HTML table and the JSON API. Fix: pass contamination payload strings through webui.inventory.scrub() (or at minimum redact_path) in _inspect_contamination so the page is internally consistent, and correct the module docstring at webui/session_loader.py line 15, which currently claims absolute paths are collapsed by inventory redaction. Keep the field itself: it is legitimate issue #630 evidence. Do not remove it. Also add regression tests in tests/test_webui_sessions_view.py for the degraded-ownership render (the file already imports STATUS_UNAVAILABLE and never uses it) and for HTML escaping of hostile session/marker values. When done: run /Users/jasonwalker/Development/Gitea-Tools/venv/bin/python -m pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q from the worktree, publish the branch, and hand back to a reviewer session. ``` WHAT_HAPPENED: Independent reviewer validation of PR #898 at head a81db754024f2339a25e1d502a92ebfa506abe5a. Confirmed the head is the two-parent merge of 619f67907726631a74e6333d8004a435130e2309 with master 7af40fb5ff7debd5e9165fe97d9c7c279358e175, 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: a81db754024f2339a25e1d502a92ebfa506abe5a REVIEW_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 a81db754024f2339a25e1d502a92ebfa506abe5a, 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 master 7af40fb5ff7debd5e9165fe97d9c7c279358e175, 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 @ `a81db754` **Decision: REQUEST_CHANGES.** Two blockers, four non-blocking notes. Reviewer `sysadmin` / `prgs-reviewer`; author `jcwalker3`. 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 from `619f679`. ## Head and effective diff `a81db754` is the two-parent merge you reported: parents `619f6790` (feature) and `7af40fb5` (master). `git merge-base 7af40fb5 a81db754` returns `7af40fb5` exactly, 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. - **Routing.** `/sessions`, `/api/sessions`, `/api/v1/sessions` are all registered `GET`-only. The nav stub entry is removed cleanly, `STUB_PAGES` no longer carries `/sessions`, and exactly one nav item resolves to it. - **Escaping.** Every interpolation in `session_views.py` passes through `html.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. - **Recovery guidance.** `SANCTIONED_RECOVERY_DOCS` names reconnect and operator-restart documentation only. The section states plainly that manual `pkill` is contamination, not recovery. No restart, kill, or takeover control exists on the page. - **Fail-soft composition.** The runtime and inventory loaders are individually guarded, and I exercised the fallback at `session_loader.py:382` directly: `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. - **Dead-PID accuracy.** `pid_alive is False` is the only thing that raises `pid-dead`; `None` stays neutral and renders as a skipped badge. Healthy records are not mislabelled. - **PR #883 interaction.** The master changes merged in through PR #883 do not touch the web UI. `tests/test_issue_854_semantic_container_exclusion.py` passes at this head: 13 passed, 8 subtests. No conflict, no regression. - **Integration consistency.** `runtime_views.py`, `system_health_views.py`, and `docs/webui-local-dev.md` are 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_dict` and `inventory_snapshot_to_dict` were already exposed at `/api/runtime` and `/api/v1/inventory` on 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 the `sessions`, `leases`, and `locks` sections without ever consulting their status. Those three are exactly `OWNERSHIP_SECTIONS` in `webui/inventory.py:61`. Reproduced directly at this head - sessions section OK, leases and locks `unavailable`: ```text ownership_authority_complete: False inventory.status: degraded row lease_ids: () worktree_paths: () renders "unbound": True renders leases "none": True per-row degraded/unavailable caveat: False ``` 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.py` documents this exact hazard on `ownership_authority_complete`: > While this is false the snapshot must not describe any work item as unowned: a lease the reader could not load is not an absent lease. The mitigations present are not sufficient to carry this. The summary bar shows a global `Inventory: degraded` badge, 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_ids` and `worktree_paths` come 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_section` and `_worktrees_section` both check `section.ok` and render a degraded badge; the sessions table is the one place that does not. A narrower, fully realistic variant: when the `work_items` table is absent the leases section is `STATUS_DEGRADED` and every lease row carries `work_kind`/`work_number` of `None`. `work_numbers` stays empty, so the lock-correlation loop can never match, and **every** session row reads `unbound` even when locks with real `worktree_path` values are present and readable. **Fix.** Thread ownership-section status into the row builder and renderer; render `unknown (inventory degraded)` instead of `none`/`unbound` when the backing section is not OK; expose `ownership_authority_complete` and per-row authority state in `snapshot_to_dict`. ## B2 (blocker) - contamination payload skips the redaction every sibling field uses This is the explicit `command_summary` judgment. **My judgment: `command_summary` is necessary evidence for issue #630 and should stay.** `reason_class` alone 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: `grep` for `command_summary` under `webui/` on master `7af40fb5` returns 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`) copies `command_summary`, `session_id`, `role`, and `reason_class` out of the payload with only `str()`. Every inventory-sourced field on the same page arrives via `webui.inventory.scrub()`, which collapses `$HOME` to `~` 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_command` is a narrow denylist, verified by running it at this head: ```text IN : pkill -f /Users/<user>/Development/Gitea-Tools/gitea_mcp_server.py OUT: pkill -f /Users/<user>/Development/Gitea-Tools/gitea_mcp_server.py <- $HOME not collapsed IN : curl -H 'X-Api-Key: SUPERSECRET123' https://gitea.prgs.cc/api OUT: curl -H 'X-Api-Key: SUPERSECRET123' https://gitea.prgs.cc/api <- not redacted IN : git push --password hunter2 origin master OUT: git push --password hunter2 origin master <- not redacted IN : PRIVATE_KEY=abc123 python deploy.py OUT: PRIVATE_KEY=abc123 python deploy.py <- not redacted IN : GITEA_TOKEN=abc123 git push OUT: GITEA_TOKEN=*** git push <- correctly redacted IN : git push https://user:[email protected]/x.git master OUT: git push https://***@gitea.prgs.cc/x.git master <- correctly redacted ``` It catches `KEY=VALUE` for 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:15` states "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`, with `assess_bind_host` refusing `0.0.0.0`) genuinely limits the audience, and I weighed it. It does not resolve the finding: `/api/v1/sessions` is 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 minimum `redact_path`) in `_inspect_contamination`, and correct the docstring. Keep the field. ## Non-blocking notes - **N1 - dead code.** `locks_by_session_hint` (`session_loader.py:241`) is populated by a `for` loop whose body is `pass`, then discarded via `_ = locks_by_session_hint` at line 331. Remove both, or implement the correlation the comment describes. - **N2 - the degraded path was meant to be tested.** `tests/test_webui_sessions_view.py:26` imports `STATUS_UNAVAILABLE` and never uses it. That is the coverage gap behind B1. There is also no escaping test and no `lease-expired` test; I verified both by hand, but they should be pinned. - **N3 - status allowlist.** `status not in {"active","alive","running","ok"}` raises a `status:<value>` stale badge for anything else. Harmless today: `upsert_session` is only called from `allocator_service.py:890`, which never passes `status`, so the value is always `active`. It becomes a mislabelling risk the moment a legitimate terminal status such as `completed` or `released` is introduced. Consider an explicit unhealthy-set instead of an allowlist. - **N4 - `webui/runtime_views.py` still 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. <!-- sph:v1 --> ## Canonical Handoff ```text REPOSITORY: Scaled-Tech-Consulting/Gitea-Tools ISSUE: 641 PR: 898 WORKFLOW_STATE: needs-author HEAD_SHA: a81db754024f2339a25e1d502a92ebfa506abe5a BASE_BRANCH: master BASE_OR_MERGE_SHA: 7af40fb5ff7debd5e9165fe97d9c7c279358e175 ACTING_ROLE: reviewer ACTING_IDENTITY: sysadmin (prgs-reviewer) COMPLETED_ACTIONS: Acquired reviewer lease 2025-803cbd7aed28 at the exact head (comments 16482, re-acquired 16493 after TTL expiry). Verified the head is the two-parent merge of 619f6790 and master 7af40fb5 and that merge-base equals master head. Reviewed all 8 changed files. Ran focused, full web UI, Issue #854, and a 13-file runtime/health/inventory/restart set against the unmodified PR head, plus a clean baseline comparison at master. Exercised escaping, degraded-ownership rendering, the fail-soft inventory fallback, and the write-time command redactor directly. Submitted REQUEST_CHANGES pinned to a81db754024f2339a25e1d502a92ebfa506abe5a. VALIDATION_EVIDENCE: Review worktree /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr898-619f679 at a81db754024f2339a25e1d502a92ebfa506abe5a, clean before and after. 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. The 13-file runtime/health set gave 1 failed, 223 passed; baseline worktree /Users/jasonwalker/Development/Gitea-Tools/branches/mergesim-885 at 7af40fb5ff7debd5e9165fe97d9c7c279358e175 gave 1 failed, 223 passed with identical test and assertion, so baseline-equivalent failure accepted. Baseline webui gave 486 passed, 374 subtests; the +12 test and -2 subtest delta is fully explained by /sessions leaving STUB_PAGES, iterated twice in tests/test_webui_shell.py. MUTATION_LEDGER: Gitea: reviewer lease acquisition comments 16482 and 16493; one REQUEST_CHANGES review at a81db754024f2339a25e1d502a92ebfa506abe5a; reviewer lease release. Local worktree/index: git checkout --detach a81db754024f2339a25e1d502a92ebfa506abe5a in branches/review-pr898-619f679 (from 619f67907726631a74e6333d8004a435130e2309) and git checkout --detach 7af40fb5ff7debd5e9165fe97d9c7c279358e175 in branches/mergesim-885 (from 2adf612c9600e8c341c881b075280ea1ac908a22). No file edits, no git ref mutations, no branch or push mutations, no merge. BLOCKERS: B1 degraded ownership inventory rendered as affirmative none/unbound with no per-row caveat, contradicting the ownership_authority_complete invariant; B2 contamination payload fields bypass webui.inventory.scrub, newly exposing $HOME paths and denylist-missed secrets on the web UI and JSON API, with a module docstring claiming otherwise. NEXT_ACTOR: author NEXT_ACTION: Fix B1 and B2, add the degraded-ownership and escaping regression tests, push, and request a fresh independent review at the new head. PROHIBITED_ACTIONS: Do not approve or merge this PR from the author role. Do not rebase or force-push the branch. Do not remove command_summary; redact it instead. Do not reuse this verdict at any other head. NEXT_PROMPT: Author task for Scaled-Tech-Consulting/Gitea-Tools, Issue #641, PR #898, branch feat/issue-641-runtime-session-view, at reviewed head a81db754024f2339a25e1d502a92ebfa506abe5a. A reviewer submitted REQUEST_CHANGES with two blockers. Work under the prgs-author profile in the existing worktree /Users/jasonwalker/Development/Gitea-Tools/branches/feat-issue-641-runtime-session-view and take the issue lock first. B1: webui/session_loader.py::_build_session_rows ignores the status of the sessions, leases, and locks sections, so when leases or locks are degraded or unavailable every row still renders lease_ids as none and worktree_paths as unbound, asserting absence where the reader simply could not load the data; this contradicts the ownership_authority_complete invariant documented in webui/inventory.py. Follow the pattern already used by _namespaces_section and _worktrees_section, which check section.ok; render unknown (inventory degraded) instead, and add ownership_authority_complete plus per-row authority state to snapshot_to_dict. B2: webui/session_loader.py::_inspect_contamination copies command_summary, session_id, role, and reason_class from the marker payload with only str(), bypassing webui.inventory.scrub which every other field on the page uses; the write-time redactor stable_branch_push_guard.redact_command leaves absolute $HOME paths, -H 'X-Api-Key: value', --password value, and PRIVATE_KEY=value intact. Pass those strings through webui.inventory.scrub or redact_path, and fix the docstring at webui/session_loader.py line 15 which claims absolute paths are collapsed. Keep the command_summary field; it is legitimate issue #630 evidence. Add regression tests in tests/test_webui_sessions_view.py for the degraded-ownership render (the file already imports STATUS_UNAVAILABLE unused) and for HTML escaping of hostile values. Then run /Users/jasonwalker/Development/Gitea-Tools/venv/bin/python -m pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q from that worktree, publish the branch, and hand back to a reviewer session. WORKFLOW_FAILURE_ISSUES: none LAST_UPDATED: 2026-07-25T04:33:00Z ```
Owner

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: a81db75402
target_branch: master
target_branch_sha: 7af40fb5ff
last_activity: 2026-07-25T04:34:45Z
expires_at: 2026-07-25T04:44:45Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> 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: a81db754024f2339a25e1d502a92ebfa506abe5a target_branch: master target_branch_sha: 7af40fb5ff7debd5e9165fe97d9c7c279358e175 last_activity: 2026-07-25T04:34:45Z expires_at: 2026-07-25T04:44:45Z blocker: manual-release
jcwalker3 added 1 commit 2026-07-25 00:26:23 -05:00
Addresses the two blockers from the PR #898 review at a81db754.

B1 - degraded ownership inventory was rendered as affirmative absence.
_build_session_rows read the sessions/leases/locks sections without
consulting their status, so a session row emitted lease_ids=() and
worktree_paths=() whether the session genuinely held nothing or the
lease store simply could not be read. The renderer printed both as
"none" and "unbound", contradicting the ownership_authority_complete
invariant documented on InventorySnapshot.

SessionRow now carries lease_authority and worktree_authority. A
worktree binding is correlated through lease work numbers, so it is
unproven when either the leases or the locks section fails to read --
this covers the narrow variant where locks hold real worktree paths but
a degraded leases section leaves work_numbers empty. The renderer emits
"unknown (inventory <status>)" with an authority-unproven badge instead
of none/unbound, the card names the unreadable sections, and an empty
session list from an unreadable sessions section no longer reads as
"no sessions recorded". snapshot_to_dict exports
ownership_authority_complete, ownership_section_status, and per-row
lease_authority / worktree_authority so /api/sessions consumers can
distinguish the two cases.

B2 - contamination payload strings bypassed redaction.
_inspect_contamination copied command_summary, session_id, role and
reason_class out of the marker payload with only str(), while every
inventory-sourced field on the same page arrives through
webui.inventory.scrub(). The write-time redactor
stable_branch_push_guard.redact_command is a narrow denylist that leaves
absolute $HOME paths, -H 'X-Api-Key: <value>', --password <value>, and
PRIVATE_KEY=<value> intact, and this is the first web surface to render
command_summary at all.

Adds webui.inventory.scrub_text(), which collapses $HOME and redacts
credential-shaped tokens and URL userinfo anywhere inside a string rather
than only at its start, and routes the marker payload through it. scrub()
and every existing caller are untouched. The command_summary field is
kept: it is the #630 evidence naming which daemon was killed. The module
docstring claiming absolute paths were already collapsed is corrected.

Also: removes the locks_by_session_hint dead loop and its discard (N1),
adds the missing trailing newline to webui/runtime_views.py (N4), drops
an unused dataclasses.field import, and documents both honesty rules in
docs/webui-local-dev.md.

Tests: tests/test_webui_sessions_view.py grows from 12 to 26 cases,
covering degraded and unavailable ownership sections in both the HTML and
JSON paths, the locks-readable/leases-degraded variant, a guard against
over-correcting clean inventory into "unknown", the previously untested
expired-lease flag, HTML escaping of hostile values in clean and degraded
renders, and each secret class the write-time denylist misses. The
STATUS_UNAVAILABLE import that was present but unused is now exercised.

Validation, from the issue worktree with venv/bin/python (Python 3.14.5,
pytest 9.1.1):

  pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q
    -> 512 passed, 376 subtests (was 498 / 372; +14 new tests)
  pytest tests/test_issue_854_semantic_container_exclusion.py -q
    -> 13 passed, 8 subtests
  13-file runtime/health/inventory/restart set
    -> 1 failed, 223 passed; the single failure is
       test_runtime_clarity.py::TestRuntimeClarity::
       test_activate_profile_succeeds_when_enabled, the identical test and
       assertion the reviewer recorded on master at 7af40fb5, so it is
       baseline-equivalent and not introduced here.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
jcwalker3 added 1 commit 2026-07-25 00:27:12 -05:00
Author
Owner

Author remediation of review 586 — B1 and B2 addressed at head d5d121a2

[THREAD STATE LEDGER]

what is true now

  • Server-side decision state: the only formal verdict on PR #898 is REQUEST_CHANGES (review 586) by sysadmin (prgs-reviewer) pinned to former head a81db754. No approve verdict exists at any head; approval_at_current_head is false and has_blocking_change_requests was true at that head.
  • Local verdict/state: both reviewer blockers are remediated in commit 1ca2b504. The branch base was then brought current through native update-by-merge, producing exact head d5d121a2. assess_pr_sync_status at d5d121a2 reports mergeable: true, has_conflicts: false, commits_behind: 0, branch_protection_requires_current_base: false.
  • Author-side verification at the exact head: pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q gives 512 passed, 376 subtests (the reviewer's baseline at a81db754 was 498 / 372; the delta is the 14 new regression cases). pytest tests/test_issue_854_semantic_container_exclusion.py tests/test_drain_proof.py -q gives 74 passed, 64 subtests.
  • Author identity is jcwalker3 / prgs-author. No approve or merge capability was used or is held by this session.

what changed

  • Blocker B1 — degraded ownership inventory presented as affirmative absence. SessionRow now carries lease_authority and worktree_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 the leases or the locks section fails to read — this is the narrow variant the review described, where readable locks still yield unbound for every row. _render_session_row emits unknown (inventory <status>) with an authority unproven badge instead of none / 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 unreadable sessions section no longer renders as "no control-plane sessions recorded". snapshot_to_dict exports ownership_authority_complete, ownership_section_status, and per-row lease_authority / worktree_authority / ownership_note, so /api/sessions consumers can separate "holds none" from "could not be read".
  • Blocker B2 — contamination payload text bypassing redaction. Added webui.inventory.scrub_text(), which collapses $HOME and redacts credential-shaped tokens and URL userinfo anywhere inside a string rather than only at its start, and routed command_summary, session_id, role, reason_class, and the envelope summary through it in _inspect_contamination. Verified against each case the review demonstrated surviving stable_branch_push_guard.redact_command: the $HOME path 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. The command_summary field is kept, per the review's judgment that it is necessary issue #630 evidence. The module docstring at webui/session_loader.py that claimed absolute paths were already collapsed is corrected.
  • Non-blocking notes N1 and N4 are taken: the locks_by_session_hint loop with a pass body and its _ = discard are removed, and webui/runtime_views.py gains its trailing newline. An unused dataclasses.field import was also dropped.
  • Note N2 is taken as tests: tests/test_webui_sessions_view.py grows 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 affirmative none / unbound so B1 is not over-corrected into blanket uncertainty; the previously untested lease-expired and active-lease-past-expiry flags; 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. The STATUS_UNAVAILABLE import the review flagged as present-but-unused is now exercised.
  • Note N3 (the status allowlist) is deliberately not changed in this commit. Reasoning is under "what is blocked" below.

what is blocked

  • Blocker classification: no blocker
  • Nothing prevents an independent review of head d5d121a2. The PR is at a current base with no conflicts.
  • Open judgment call left for the reviewer: N3 proposed replacing the {"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_session never passes status, so the value is always active today. Happy to change it if the reviewer prefers the denylist.
  • One test in the wider runtime set fails identically on the base: 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 at 7af40fb5. It is baseline-equivalent and is not introduced by this PR.

who/what acts next

  • Next actor: an independent reviewer session under prgs-reviewer, with an identity distinct from author jcwalker3.
  • Required action: perform a fresh independent review pinned to exact head d5d121a2, confirming that B1 and B2 are closed and that the new tests genuinely fail against the pre-fix behavior.
  • Do not do: do not reuse review 586 or any verdict scoped to 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 d5d121a21b and perform a fresh independent review of the B1 and B2 remediation.
NEXT_PROMPT:

Reviewer task for Scaled-Tech-Consulting/Gitea-Tools, Issue #641, PR #898,
branch feat/issue-641-runtime-session-view, at exact head
d5d121a21b43888b013aa2a8460510f90e771e7c.

Review 586 recorded REQUEST_CHANGES at former head a81db754 with two blockers.
The author has remediated both and brought the base current. Act under the
prgs-reviewer profile with an identity distinct from author jcwalker3. Pin the
head before deciding and do not reuse review 586 at this head.

B1 was: webui/session_loader.py::_build_session_rows ignored the status of the
sessions, leases, and locks sections, so degraded ownership sources rendered as
"none" and "unbound". Verify the fix: SessionRow.lease_authority and
SessionRow.worktree_authority are threaded from section status; worktree
authority is the worse of the leases and locks status because locks are
correlated through lease work numbers; _render_session_row emits
"unknown (inventory <status>)" instead of none/unbound; the sessions card names
the unreadable sections; and snapshot_to_dict exports
ownership_authority_complete, ownership_section_status, and per-row
lease_authority / worktree_authority. Check the fix is not over-broad: clean
inventory must still render affirmative "none" and "unbound".

B2 was: _inspect_contamination copied command_summary, session_id, role, and
reason_class from the marker payload with only str(). Verify the fix: the new
webui.inventory.scrub_text collapses $HOME and redacts credential-shaped tokens
and URL userinfo anywhere in a string, and _inspect_contamination routes every
payload string through it. Re-run the specific cases you demonstrated against
stable_branch_push_guard.redact_command -- absolute $HOME path,
-H 'X-Api-Key: <value>', --password <value>, PRIVATE_KEY=<value> -- and confirm
each is redacted on both the HTML page and /api/sessions. Confirm scrub() and
its existing callers are unchanged, and that command_summary is still present
as #630 evidence rather than removed.

Also assess whether the regression tests genuinely pin the behavior: neuter each
fix and confirm the corresponding test fails.

Validation to reproduce from a review worktree at this head, with
/Users/jasonwalker/Development/Gitea-Tools/venv/bin/python:
  pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q
  pytest tests/test_issue_854_semantic_container_exclusion.py -q
Expect 512 passed / 376 subtests and 13 passed / 8 subtests respectively. The
13-file runtime/health set still shows the baseline-equivalent
test_runtime_clarity failure you recorded at master 7af40fb5.

Note N3 (status allowlist vs explicit unhealthy set) was deliberately left
unchanged; decide whether you want it in this PR or a follow-up issue.

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 head d5d121a2. 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 master 7af40fb5, so it is baseline-equivalent and accepted. Ancestry verified directly: git merge-base --is-ancestor 2f4dec83 HEAD succeeds, so live master is fully contained in the PR head. assess_pr_sync_status at d5d121a2 reports 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)

## Author remediation of review 586 — B1 and B2 addressed at head `d5d121a2` [THREAD STATE LEDGER] **what is true now** - Server-side decision state: the only formal verdict on PR #898 is REQUEST_CHANGES (review 586) by `sysadmin` (`prgs-reviewer`) pinned to former head `a81db754`. No approve verdict exists at any head; `approval_at_current_head` is false and `has_blocking_change_requests` was true at that head. - Local verdict/state: both reviewer blockers are remediated in commit `1ca2b504`. The branch base was then brought current through native update-by-merge, producing exact head `d5d121a2`. `assess_pr_sync_status` at `d5d121a2` reports `mergeable: true`, `has_conflicts: false`, `commits_behind: 0`, `branch_protection_requires_current_base: false`. - Author-side verification at the exact head: `pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q` gives 512 passed, 376 subtests (the reviewer's baseline at `a81db754` was 498 / 372; the delta is the 14 new regression cases). `pytest tests/test_issue_854_semantic_container_exclusion.py tests/test_drain_proof.py -q` gives 74 passed, 64 subtests. - Author identity is `jcwalker3` / `prgs-author`. No approve or merge capability was used or is held by this session. **what changed** - Blocker B1 — degraded ownership inventory presented as affirmative absence. `SessionRow` now carries `lease_authority` and `worktree_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* the `leases` or the `locks` section fails to read — this is the narrow variant the review described, where readable locks still yield `unbound` for every row. `_render_session_row` emits `unknown (inventory <status>)` with an *authority unproven* badge instead of `none` / `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 unreadable `sessions` section no longer renders as "no control-plane sessions recorded". `snapshot_to_dict` exports `ownership_authority_complete`, `ownership_section_status`, and per-row `lease_authority` / `worktree_authority` / `ownership_note`, so `/api/sessions` consumers can separate "holds none" from "could not be read". - Blocker B2 — contamination payload text bypassing redaction. Added `webui.inventory.scrub_text()`, which collapses `$HOME` and redacts credential-shaped tokens and URL userinfo *anywhere inside* a string rather than only at its start, and routed `command_summary`, `session_id`, `role`, `reason_class`, and the envelope summary through it in `_inspect_contamination`. Verified against each case the review demonstrated surviving `stable_branch_push_guard.redact_command`: the `$HOME` path 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. The `command_summary` field is kept, per the review's judgment that it is necessary issue #630 evidence. The module docstring at `webui/session_loader.py` that claimed absolute paths were already collapsed is corrected. - Non-blocking notes N1 and N4 are taken: the `locks_by_session_hint` loop with a `pass` body and its `_ =` discard are removed, and `webui/runtime_views.py` gains its trailing newline. An unused `dataclasses.field` import was also dropped. - Note N2 is taken as tests: `tests/test_webui_sessions_view.py` grows 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 affirmative `none` / `unbound` so B1 is not over-corrected into blanket uncertainty; the previously untested `lease-expired` and `active-lease-past-expiry` flags; 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. The `STATUS_UNAVAILABLE` import the review flagged as present-but-unused is now exercised. - Note N3 (the status allowlist) is deliberately not changed in this commit. Reasoning is under "what is blocked" below. **what is blocked** - Blocker classification: no blocker - Nothing prevents an independent review of head `d5d121a2`. The PR is at a current base with no conflicts. - Open judgment call left for the reviewer: N3 proposed replacing the `{"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_session` never passes `status`, so the value is always `active` today. Happy to change it if the reviewer prefers the denylist. - One test in the wider runtime set fails identically on the base: `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 at `7af40fb5`. It is baseline-equivalent and is not introduced by this PR. **who/what acts next** - Next actor: an independent reviewer session under `prgs-reviewer`, with an identity distinct from author `jcwalker3`. - Required action: perform a fresh independent review pinned to exact head `d5d121a2`, confirming that B1 and B2 are closed and that the new tests genuinely fail against the pre-fix behavior. - Do not do: do not reuse review 586 or any verdict scoped to `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 d5d121a21b43888b013aa2a8460510f90e771e7c and perform a fresh independent review of the B1 and B2 remediation. NEXT_PROMPT: ```text Reviewer task for Scaled-Tech-Consulting/Gitea-Tools, Issue #641, PR #898, branch feat/issue-641-runtime-session-view, at exact head d5d121a21b43888b013aa2a8460510f90e771e7c. Review 586 recorded REQUEST_CHANGES at former head a81db754 with two blockers. The author has remediated both and brought the base current. Act under the prgs-reviewer profile with an identity distinct from author jcwalker3. Pin the head before deciding and do not reuse review 586 at this head. B1 was: webui/session_loader.py::_build_session_rows ignored the status of the sessions, leases, and locks sections, so degraded ownership sources rendered as "none" and "unbound". Verify the fix: SessionRow.lease_authority and SessionRow.worktree_authority are threaded from section status; worktree authority is the worse of the leases and locks status because locks are correlated through lease work numbers; _render_session_row emits "unknown (inventory <status>)" instead of none/unbound; the sessions card names the unreadable sections; and snapshot_to_dict exports ownership_authority_complete, ownership_section_status, and per-row lease_authority / worktree_authority. Check the fix is not over-broad: clean inventory must still render affirmative "none" and "unbound". B2 was: _inspect_contamination copied command_summary, session_id, role, and reason_class from the marker payload with only str(). Verify the fix: the new webui.inventory.scrub_text collapses $HOME and redacts credential-shaped tokens and URL userinfo anywhere in a string, and _inspect_contamination routes every payload string through it. Re-run the specific cases you demonstrated against stable_branch_push_guard.redact_command -- absolute $HOME path, -H 'X-Api-Key: <value>', --password <value>, PRIVATE_KEY=<value> -- and confirm each is redacted on both the HTML page and /api/sessions. Confirm scrub() and its existing callers are unchanged, and that command_summary is still present as #630 evidence rather than removed. Also assess whether the regression tests genuinely pin the behavior: neuter each fix and confirm the corresponding test fails. Validation to reproduce from a review worktree at this head, with /Users/jasonwalker/Development/Gitea-Tools/venv/bin/python: pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q pytest tests/test_issue_854_semantic_container_exclusion.py -q Expect 512 passed / 376 subtests and 13 passed / 8 subtests respectively. The 13-file runtime/health set still shows the baseline-equivalent test_runtime_clarity failure you recorded at master 7af40fb5. Note N3 (status allowlist vs explicit unhealthy set) was deliberately left unchanged; decide whether you want it in this PR or a follow-up issue. ``` 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 head d5d121a2. 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 d5d121a21b43888b013aa2a8460510f90e771e7c, 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 master 7af40fb5, so it is baseline-equivalent and accepted. Ancestry verified directly: git merge-base --is-ancestor 2f4dec83 HEAD succeeds, so live master is fully contained in the PR head. assess_pr_sync_status at d5d121a2 reports 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)
Owner

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: d5d121a21b
target_branch: master
target_branch_sha: 2f4dec8323
last_activity: 2026-07-25T05:53:15Z
expires_at: 2026-07-25T06:03:15Z
blocker: none

<!-- mcp-review-lease:v1 --> 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: d5d121a21b43888b013aa2a8460510f90e771e7c target_branch: master target_branch_sha: 2f4dec832327513118f2fe92b74da25d124a01cb last_activity: 2026-07-25T05:53:15Z expires_at: 2026-07-25T06:03:15Z blocker: none
sysadmin approved these changes 2026-07-25 00:56:09 -05:00
sysadmin left a comment
Owner

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_rows now derives lease_authority from the leases section status and worktree_authority from the worse of leases+locks, and threads both onto every SessionRow.

Verified independently, with my own InventorySection fixtures driving the real render and API paths:

Scenario Result
Narrow variant — locks readable with real worktree_path, leases degraded row reads unknown (inventory degraded) + authority unproven badge; unbound absent from the page
locks unavailable, leases ok worktree cell unknown (inventory unavailable); lease id still rendered affirmatively
Over-broad guard — fully clean inventory still renders affirmative none and unbound, no badge, no caveat card
unreadable sessions section no longer renders "No control-plane sessions recorded"; states the list could not be read

The sessions card names the unreadable sections. snapshot_to_dict exports ownership_authority_complete, ownership_section_status, and per-row lease_authority / worktree_authority / ownership_note; /api/sessions reports ownership_authority_complete: false with per-section status in the degraded cases.

The narrow variant is the important one and it holds: with locks readable but leases degraded, work_numbers is empty, so the correlation loop yields no path — previously rendered as the affirmative unbound, now correctly unknown.

B2 — contamination redaction: RESOLVED

webui.inventory.scrub_text() collapses $HOME and redacts credential-shaped tokens and URL userinfo anywhere inside a string. _inspect_contamination routes 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:

abs $HOME path      -> ~/Development/Gitea-Tools/place/run.sh
-H 'X-Api-Key: ...' -> curl -H 'X-Api-Key: [redacted]' https://gitea.prgs.cc
--password ...      -> mysql --password [redacted] --host db
PRIVATE_KEY=...     -> env PRIVATE_KEY=[redacted] run.sh
URL userinfo        -> git clone https://[redacted]@gitea.prgs.cc/x/y.git

command_summary is retained as #630 evidence, not removed. The session_loader module docstring no longer claims absolute paths are already collapsed by inventory redaction.

scrub() and every existing caller are unchanged — webui/inventory.py carries 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_command leaks 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:

Neutered Result
B1 authority threading (forced STATUS_OK) 4 failed, incl. test_readable_locks_are_not_reported_unbound_when_leases_degrade
B2 scrub_text -> bare str() 6 failed, incl. subtests for every secret class
unreadable-sessions-list branch 1 failedtest_missing_session_list_is_not_reported_as_no_sessions

The tests fail when the behavior regresses. The worktree was restored clean after each mutation; final git status empty at d5d121a2.

Validation reproduced

From a session-owned review worktree at this exact head, with venv/bin/python:

pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q
  -> 512 passed, 376 subtests            (matches claim exactly; +14 vs 498/372)
pytest tests/test_issue_854_semantic_container_exclusion.py -q
  -> 13 passed, 8 subtests               (matches claim exactly)

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_enabled passes in that batch but fails in isolation — order-dependent. Confirmed baseline-equivalent: the identical test fails identically on master 7af40fb5. Not introduced by this PR.

Scope

Nine files, all #641. Routes /sessions, /api/sessions, /api/v1/sessions are 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)

  • N3 — {"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.
  • N5 (new, cosmetic). _combined_authority returns the first non-ok status, not the worst, though its docstring says "Worst status". With leases degraded and locks unavailable the label reads degraded. Safety behavior is unaffected — any non-ok triggers the unknown cell — 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:

Merge PR #898 (issue #641) on remote=prgs, org Scaled-Tech-Consulting, repo Gitea-Tools, pinned to expected_head_sha d5d121a21b43888b013aa2a8460510f90e771e7c. The approval review by sysadmin / prgs-reviewer is valid at this exact head. Confirm mergeable and commits_behind 0 via gitea_assess_pr_sync_status before acting; if the head has moved, stop and request a fresh review instead.

WHAT_HAPPENED:
The author remediated both blockers from review 586 in commit 1ca2b504 and brought the base current via update-by-merge, producing head d5d121a2. 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:
d5d121a21b

REVIEW_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

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_rows` now derives `lease_authority` from the leases section status and `worktree_authority` from the worse of leases+locks, and threads both onto every `SessionRow`. Verified independently, with my own `InventorySection` fixtures driving the real render and API paths: | Scenario | Result | |---|---| | **Narrow variant** — locks readable with real `worktree_path`, leases degraded | row reads `unknown (inventory degraded)` + *authority unproven* badge; `unbound` absent from the page | | locks unavailable, leases ok | worktree cell `unknown (inventory unavailable)`; lease id still rendered affirmatively | | **Over-broad guard** — fully clean inventory | still renders affirmative `none` and `unbound`, no badge, no caveat card | | unreadable sessions section | no longer renders "No control-plane sessions recorded"; states the list could not be read | The sessions card names the unreadable sections. `snapshot_to_dict` exports `ownership_authority_complete`, `ownership_section_status`, and per-row `lease_authority` / `worktree_authority` / `ownership_note`; `/api/sessions` reports `ownership_authority_complete: false` with per-section status in the degraded cases. The narrow variant is the important one and it holds: with locks readable but leases degraded, `work_numbers` is empty, so the correlation loop yields no path — previously rendered as the affirmative `unbound`, now correctly `unknown`. ## B2 — contamination redaction: RESOLVED `webui.inventory.scrub_text()` collapses `$HOME` and redacts credential-shaped tokens and URL userinfo anywhere inside a string. `_inspect_contamination` routes 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`: ``` abs $HOME path -> ~/Development/Gitea-Tools/place/run.sh -H 'X-Api-Key: ...' -> curl -H 'X-Api-Key: [redacted]' https://gitea.prgs.cc --password ... -> mysql --password [redacted] --host db PRIVATE_KEY=... -> env PRIVATE_KEY=[redacted] run.sh URL userinfo -> git clone https://[redacted]@gitea.prgs.cc/x/y.git ``` `command_summary` is retained as `#630` evidence, not removed. The `session_loader` module docstring no longer claims absolute paths are already collapsed by inventory redaction. `scrub()` and every existing caller are unchanged — `webui/inventory.py` carries **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_command` leaks 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: | Neutered | Result | |---|---| | B1 authority threading (forced `STATUS_OK`) | **4 failed**, incl. `test_readable_locks_are_not_reported_unbound_when_leases_degrade` | | B2 `scrub_text` -> bare `str()` | **6 failed**, incl. subtests for every secret class | | unreadable-sessions-list branch | **1 failed** — `test_missing_session_list_is_not_reported_as_no_sessions` | The tests fail when the behavior regresses. The worktree was restored clean after each mutation; final `git status` empty at `d5d121a2`. ## Validation reproduced From a session-owned review worktree at this exact head, with `venv/bin/python`: ``` pytest tests/test_webui_sessions_view.py tests/test_webui_*.py -q -> 512 passed, 376 subtests (matches claim exactly; +14 vs 498/372) pytest tests/test_issue_854_semantic_container_exclusion.py -q -> 13 passed, 8 subtests (matches claim exactly) ``` 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_enabled` passes in that batch but fails in isolation — order-dependent. Confirmed baseline-equivalent: the identical test fails identically on master `7af40fb5`. Not introduced by this PR. ## Scope Nine files, all `#641`. Routes `/sessions`, `/api/sessions`, `/api/v1/sessions` are 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) - **N3 — `{"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. - **N5 (new, cosmetic).** `_combined_authority` returns the *first* non-`ok` status, not the worst, though its docstring says "Worst status". With leases `degraded` and locks `unavailable` the label reads `degraded`. Safety behavior is unaffected — any non-`ok` triggers the `unknown` cell — 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: ```text Merge PR #898 (issue #641) on remote=prgs, org Scaled-Tech-Consulting, repo Gitea-Tools, pinned to expected_head_sha d5d121a21b43888b013aa2a8460510f90e771e7c. The approval review by sysadmin / prgs-reviewer is valid at this exact head. Confirm mergeable and commits_behind 0 via gitea_assess_pr_sync_status before acting; if the head has moved, stop and request a fresh review instead. ``` WHAT_HAPPENED: The author remediated both blockers from review 586 in commit 1ca2b504 and brought the base current via update-by-merge, producing head d5d121a2. 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: d5d121a21b43888b013aa2a8460510f90e771e7c REVIEW_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
Owner

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: d5d121a21b
target_branch: master
target_branch_sha: 2f4dec8323
last_activity: 2026-07-25T06:01:20Z
expires_at: 2026-07-25T06:11:20Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> 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: d5d121a21b43888b013aa2a8460510f90e771e7c target_branch: master target_branch_sha: 2f4dec832327513118f2fe92b74da25d124a01cb last_activity: 2026-07-25T06:01:20Z expires_at: 2026-07-25T06:11:20Z blocker: manual-release
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/issue-641-runtime-session-view:feat/issue-641-runtime-session-view
git checkout feat/issue-641-runtime-session-view
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#898