Skip to content

Latest commit

Β 

History

History
155 lines (134 loc) Β· 8.35 KB

File metadata and controls

155 lines (134 loc) Β· 8.35 KB
phase quick-260707-s44
plan 1
type execute
wave 1
depends_on
files_modified
src/phaze/routers/admin_agents.py
tests/agents/routers/test_admin_agents.py
autonomous true
requirements
QUICK-260707-s44
must_haves
truths artifacts key_links
Revoked agents (revoked_at IS NOT NULL) never appear in the /admin/agents panel or its /_table HTMX poll partial
The permanently-revoked legacy-application-server seed row is excluded from the operator agents panel
The legacy-application-server Agent row still exists in the DB (FK default owner) β€” only its display is suppressed
Non-revoked agents (alive/stale/dead/never) still render and sort correctly
path provides contains
src/phaze/routers/admin_agents.py
_load_agents with revoked_at IS NULL filter
Agent.revoked_at.is_(None)
path provides
tests/agents/routers/test_admin_agents.py
Regression test asserting a revoked agent is absent + reconciled 5-state/sort-order tests
from to via pattern
src/phaze/routers/admin_agents.py:_load_agents
Agent.revoked_at
select(Agent).where(Agent.revoked_at.is_(None))
revoked_at\.is_\(None\)
Fix `legacy-application-server` (and any revoked agent) leaking into the `/admin/agents` operator panel. `_load_agents()` currently runs `select(Agent)` with NO `revoked_at` filter, so the permanently-revoked legacy seed row shows up in the "Agents Β· heartbeating" panel. Every other agent query in the codebase (main.py, shell.py, pipeline.py) already filters revoked agents via `.where(Agent.revoked_at.is_(None))`. Add the same filter here.

Purpose: Operator panel should only show live/heartbeating agents, not the permanently-revoked FK-placeholder row. Output: One-line filter change in _load_agents + reconciled/added tests.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/STATE.md

@src/phaze/routers/admin_agents.py @src/phaze/models/agent.py @src/phaze/services/agent_liveness.py @tests/agents/routers/test_admin_agents.py

From src/phaze/models/agent.py:

  • LEGACY_AGENT_ID = "legacy-application-server" (module constant)
  • Agent.revoked_at: Mapped[datetime | None] β€” nullable timezone-aware column; NULL means "not revoked"

From src/phaze/routers/admin_agents.py (the ONLY line to change):

  • _load_agents(session) line ~68: result = await session.execute(select(Agent))
  • BOTH page() (full-page + HX-Request partial) and table_partial() (/_table poll) call _load_agents, so a single filter change covers all render paths.

From src/phaze/services/agent_liveness.py:

  • classify(agent, now) still returns "revoked" when revoked_at IS NOT NULL, and sort_key still has the revoked tier. These stay as-is (reachable if an agent is revoked mid-load elsewhere). Out of scope to remove β€” just note they become effectively unreachable via this panel.

From tests/conftest.py (IMPORTANT nuance):

  • The async_engine fixture seeds the legacy row via Agent(id=LEGACY_AGENT_ID, name=LEGACY_AGENT_ID, scan_roots=[]) WITHOUT setting revoked_at. So in the TEST DB the legacy row is a NEVER agent (not revoked), unlike production where alembic migration 012 stamps revoked_at == created_at. => The absence regression test MUST assert on an EXPLICITLY-revoked agent (the smoke fixture's RevokedBox / id revoked-agent), NOT on the legacy row.
Task 1: Filter revoked agents out of _load_agents src/phaze/routers/admin_agents.py In `_load_agents` (~line 68), change `select(Agent)` to `select(Agent).where(Agent.revoked_at.is_(None))`, matching the established codebase pattern in src/phaze/main.py, src/phaze/routers/shell.py, and src/phaze/routers/pipeline.py. This is the ONLY source change. Do NOT modify the classify/sort_key revoked handling in agent_liveness.py, the REVOKED status pill template, or the legacy Agent row itself β€” the legacy-application-server row must keep existing in the DB (it is the FK default owner for file.agent_id and scan_batch.agent_id with ondelete=RESTRICT). Update the `_load_agents` docstring's "Load every Agent" phrasing to reflect that revoked agents are now excluded (mention the shared `revoked_at IS NULL` convention). If a leading comment or module docstring elsewhere claims revoked agents "land last" in this panel, adjust wording so it is not self-contradictory (they no longer appear). cd /Users/Robert/orca/workspaces/phaze/post-deploy-debug-2 && grep -q "revoked_at.is_(None)" src/phaze/routers/admin_agents.py && uv run mypy src/phaze/routers/admin_agents.py `_load_agents` executes `select(Agent).where(Agent.revoked_at.is_(None))`; mypy clean; legacy row untouched in DB. Task 2: Add revoked-agent absence regression + reconcile 5-state and sort-order tests tests/agents/routers/test_admin_agents.py Reconcile the two tests that previously expected the revoked agent to render, and add a new absence regression test. The `smoke` fixture seeds an explicitly-revoked `RevokedBox` (id `revoked-agent`, revoked_at=now) plus alive/stale/dead/never agents β€” reuse it.
1. Add `test_revoked_agent_absent`: GET /admin/agents/_table (and optionally GET /admin/agents),
   assert `"RevokedBox" not in body` and `'aria-label="Status: revoked"' not in body`, while asserting
   a non-revoked control (e.g. `"AliveBox" in body`) IS present β€” proving the filter excludes only
   revoked rows, not the whole table. This is the core regression guard for the leak.

2. Reconcile `test_status_pills_render_all_5_states` (~line 154): the revoked pill can no longer render
   in this panel. Remove the `"REVOKED" in body` assertion (line ~172) and rename/redoc the test to
   cover the 4 states that DO render (alive/stale/dead/never). Keep the gray-100/dark:gray-800 assertion
   (NEVER uses the same neutral surface). Update the module docstring bullet (line ~7) accordingly.

3. Reconcile `test_sort_order` (~line 254): remove `"revoked"` from the `pos` dict (line ~265) and drop
   it from the final chained assertion so it reads
   `pos["alive"] < pos["stale"] < pos["dead"] < pos["never"]`. Update the test docstring (line ~256) and
   the module docstring bullet (line ~9) from "alive β†’ stale β†’ dead β†’ never β†’ revoked" to
   "alive β†’ stale β†’ dead β†’ never (revoked agents are filtered out of the panel)".

Do NOT weaken the empty-state or BLOCKER-2 tests. Keep line length <= 150.
cd /Users/Robert/orca/workspaces/phaze/post-deploy-debug-2 && uv run pytest tests/agents/routers/test_admin_agents.py -x -q && uv run ruff check tests/agents/routers/test_admin_agents.py New `test_revoked_agent_absent` passes; reconciled 5-state and sort-order tests pass; full test_admin_agents.py suite green; ruff clean. - `grep -n "revoked_at" src/phaze/routers/admin_agents.py` shows the new `.where(Agent.revoked_at.is_(None))` filter. - `uv run pytest tests/agents/routers/test_admin_agents.py -q` passes (including the new absence test and reconciled tests). - `uv run ruff check src/phaze/routers/admin_agents.py tests/agents/routers/test_admin_agents.py` clean. - `uv run mypy src/phaze/routers/admin_agents.py` clean. - Legacy row untouched: no DELETE/UPDATE against the Agent row anywhere in the diff.

<success_criteria>

  • Revoked agents (including the legacy placeholder in production, where revoked_at is stamped) never appear in /admin/agents or /admin/agents/_table.
  • Non-revoked agents still render and sort alive β†’ stale β†’ dead β†’ never.
  • The legacy-application-server Agent row still exists in the DB (display-only fix).
  • Regression test locks the fix; previously-passing revoked-render/sort tests reconciled to expect absence. </success_criteria>
Create `.planning/quick/260707-s44-hide-revoked-agents-legacy-application-s/260707-s44-SUMMARY.md` when done.