Skip to content

fix(ops): create new session each call and show host empty state#42

Open
LinChuang2008 wants to merge 7 commits intomainfrom
fix/ops-create-session-and-host-empty-state
Open

fix(ops): create new session each call and show host empty state#42
LinChuang2008 wants to merge 7 commits intomainfrom
fix/ops-create-session-and-host-empty-state

Conversation

@LinChuang2008
Copy link
Copy Markdown
Owner

Summary

修复 AI 运维助手页面 (/ops) 在全新安装环境下的两个相互配合的 bug,导致整页基本不可用:

Bug 1 — POST /api/v1/ops/sessions 复用空白草稿
旧实现 (backend/app/routers/ops.py:256-258) 调用 _cleanup_empty_sessions 后直接返回 reuse 结果,导致:

  • 用户每次点击 "新建会话" 拿到的都是同一个 session id
  • body.title 被静默丢弃
    修复:删除 reuse 分支,显式清理用户残留的空白草稿(无 title + 无消息)后总是创建新 session。

Bug 2 — OpsInputBar 在 hosts 空时进入死循环
frontend/src/components/ops/OpsInputBar.tsx:141{hosts.length > 0 && ...} 把整段主机选择 row 包起来。Fresh DB 没有主机时 row 消失,但 L84 的发送守卫 if (!selectedHostId) return 仍然阻止发送,L221 还在显示 "请先选择目标主机"。用户陷入一个连选择控件都看不见的死循环。
修复:始终渲染 host row。hosts.length === 0 时显示空状态 "暂无在线主机 — 请先到 主机管理 添加并启动 Agent",并保留 host picker 的所有原有逻辑。

Test Coverage

新增 backend/tests/test_ops_session_routes.py(4 个回归测试):

测试 覆盖
test_post_sessions_returns_new_id_each_call 连续两次 POST 必须返回不同 id(直接 catch 旧的 reuse bug)
test_post_sessions_persists_title body.title 必须落库
test_post_sessions_cleans_up_empty_drafts 连续 3 次 create 后 DB 只保留最后一个
test_post_sessions_preserves_titled_and_messaged_sessions 带 title 或带消息的会话不被空白清理逻辑误删

Iron Law 验证:测试在没有 backend fix 时正确失败(assert id1 != id2 命中 reuse bug),有 fix 时全部通过。命令:

docker compose exec -T backend python -m pytest tests/test_ops_session_routes.py -q
....                                                                     [100%]
4 passed, 1 warning in 15.38s

End-to-End 验证

修复前对同一个端点连续 3 次 curl POST:3 次都返回 id=4085c76b-...title=null

修复后:

调用 id title
POST {title:"first"} 5d6acde5-... first
POST {title:"second"} c6f84769-... second
POST {} 49b86321-... null

3 个不同 id,title 全部正确持久化。

文件改动

  • backend/app/routers/ops.py (+17/-3) — 替换 create_session 的 reuse 逻辑
  • frontend/src/components/ops/OpsInputBar.tsx (+21/-8) — host row 始终渲染 + 空状态
  • backend/tests/test_ops_session_routes.py (+102 新文件) — 4 个回归测试
  • CHANGELOG.md (+13) — 2026.04.08 版本条目

Test plan

  • 后端回归测试 4/4 通过
  • e2e curl 验证 3 次 POST 返回 3 个不同 id 且 title 持久化
  • git stash 验证测试在无 fix 时确实失败
  • OpsInputBar 改动经过 stash pop 验证与 PR feat:各种体验修复 #41 的 answer mode 改动无冲突
  • 部署到 dev 后人工 QA:进入 /ops,点击 "新建会话" 看是否每次得到新 session;hosts 空时是否显示空状态提示和跳转链接

🤖 Generated with Claude Code

LinChuang2008 and others added 7 commits April 8, 2026 15:07
Two bugs in the AI 运维助手 (/ops) page made it unusable on a fresh
install:

1. POST /api/v1/ops/sessions was reusing the user's existing empty
   draft via _cleanup_empty_sessions(), so clicking "新建会话"
   silently returned the same session id every time and body.title
   was dropped on the floor. Replace the reuse path with explicit
   cleanup of stale empty drafts (no title + no messages) followed
   by always creating a fresh session that respects body.title.

2. OpsInputBar wrapped the entire host-picker row in
   `{hosts.length > 0 && ...}`. On a fresh install with no hosts
   in the database the row vanished, but the send guard still
   required selectedHostId, so users saw "请先选择目标主机" with
   no way to actually select one. Always render the row and show
   an empty-state hint linking to /hosts when no hosts exist.

Add 4 regression tests covering:
- Two consecutive POSTs return different ids
- body.title is persisted
- Stale empty drafts are cleaned up on create
- Sessions with title or messages are preserved

Verified end-to-end against the running backend (3 POSTs returned
3 distinct ids with titles "first" / "second" / null) and via
git stash to confirm the tests fail without the backend fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend:
- Add DEMO_MODE and DEMO_FAULT_DELAY_SECONDS config options
- Create demo_orchestrator.py: seed data, fault injection, auto AI diagnosis
- Create demo.py router: GET /api/v1/demo/status endpoint
- Add auto_approve flag to ToolContext for demo command execution
- Register demo flow as background task in main.py lifespan

Frontend:
- Add DEMO badge to OpsAssistant header when demo mode active
- Add global alert bar to AppLayout with auto-redirect to OpsAssistant
- Add getDemoStatus API to opsApi.ts
- Auto-select demo session and poll demo phase

Infrastructure:
- Create docker-compose.demo.yml with DEMO_MODE=true

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… TLS, host override

- Restore with_for_update() on first-user admin check (race condition fix)
- Demo auto_approve now uses safe command whitelist instead of blanket approve
- Demo state synced to Redis for multi-worker consistency
- Host override only applies when LLM omits host_id (no silent overwrite)
- Draft session cleanup skips recently active sessions
- restore_yum function defined before usage in install script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… hardening

- notifications.py: add missing datetime/timezone import (NameError fix)
- settings.py: add missing HTTPException import (NameError fix)
- service_checker.py: remove verify=False on httpx client
- main.py: remove hardcoded production IP from CORS origins
- docker-compose.prod.yml: POSTGRES_PASSWORD now required (no default fallback)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… cleanup

- ruff --fix: removed 181 unused imports across backend
- requirements.txt: bump cryptography>=46.0.6, asyncssh>=2.14.2, PyJWT>=2.12.0,
  python-multipart==0.0.22, fastmcp>=3.2.0 (5 CVE fixes)
- npm audit fix: resolved 11 vulnerabilities (including critical axios SSRF)
- Removed 5 unused npm deps: @dnd-kit/*, @monaco-editor/react, @types/react-grid-layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant