Files
SuperBizAgent-java/openspec/changes/archive/2026-07-10-session-run-trace-isolation/proposal.md
T

87 lines
5.6 KiB
Markdown

# Change: Session / Run / Trace Isolation
## Problem
The current MVP uses the same `sessionId` for two different concepts:
- Redis `SessionContext` keeps multi-turn chat history for prompt context.
- MySQL `diagnosis_session`, `agent_step`, and `tool_invocation` persist diagnosis trace data for replay, verification, feedback, and evaluation.
End-to-end verification showed that two `/api/chat` calls with the same `sessionId` correctly reuse Redis context, but MySQL trace data is mixed under the same key:
- `diagnosis_session.query` is overwritten by the second round.
- `agent_step` and `tool_invocation` append rows from both rounds under the same `session_id`.
- Trace, verifier/evaluation, and feedback can read cross-round evidence.
This makes a trace no longer represent one replayable diagnosis run.
## Proposed Solution
Introduce a stable split between conversation state and execution state:
- `chat_session`: conversation metadata keyed by `session_id`.
- `diagnosis_run`: one execution/run keyed by `run_id`, belonging to a `session_id`.
- `agent_step` and `tool_invocation`: keep existing trace detail role, add `run_id` while retaining `session_id` for compatibility and coarse filtering.
`runId` becomes an official API field:
- `/api/chat` returns `sessionId + runId`.
- `/api/ai_ops` emits an SSE-compatible metadata message containing `sessionId` and `runId` before report content.
- `GET /api/diagnosis/{sessionId}/trace` defaults to the latest run for compatibility.
- `GET /api/diagnosis/{sessionId}/trace?runId=run-...` returns the specified run after validating it belongs to the path `sessionId`.
- Feedback prefers `runId`; missing `runId` temporarily falls back to the latest run and returns both `fallbackToLatestRun=true` and the actual bound `runId`.
Trace remains an aggregate view of `diagnosis_run + agent_step + tool_invocation`; this change does not introduce a separate `diagnosis_trace` or `trace_event` table.
## Scope
- Add Flyway migrations and JPA entities/repositories for `chat_session` and `diagnosis_run`.
- Add nullable `run_id` to `agent_step` and `tool_invocation`, backfill historical data, then switch new writes to require run context.
- Move new Chat writes from `diagnosis_session` to `chat_session + diagnosis_run`.
- Update trace reads to resolve latest run or specified run.
- Add lightweight run list API: `GET /api/chat/session/{sessionId}/runs`.
- Update feedback and case-library creation to bind new data to `run_id`.
- Update AIOps to create and expose `runId` before this change is considered production complete.
- Update demo scripts and Trace UI with minimal `runId` support.
- Update relevant MVP table and architecture documentation.
- Verify with focused tests, an E2E multi-turn run using Maven when needed, logs under `logs/`, database queries via `scripts/query_mysql.py`, and baseline drift checks.
## Non-Goals
- Do not add `diagnosis_trace` or `trace_event` in this change.
- Do not implement a full run-list UI.
- Do not remove the historical `diagnosis_session` table in this change.
- Do not change the Redis conversation window strategy.
- Do not persist full conversation history in MySQL; `chat_session` stores metadata only.
- Do not split historical mixed traces into true historical runs when the original run boundary is unavailable.
## Devflow Context Constraints
- `session-storage` established the current trace tables and decided that `sessionId` is propagated through `RunnableConfig.metadata` with `SessionContextHolder` as a fallback for tools.
- `confidence-feedback` established that useful feedback creates `case_library` from `DiagnosisSession.answer`, and that `feedback` does not change execution `status`.
- `mvp-demo-trace-acceptance` established `GET /api/diagnosis/{sessionId}/trace` as a read-only endpoint and demo scripts as part of the observable story.
- `aiops-traceable-diagnosis-entry` established `/api/ai_ops` as a traceable SSE entry point and made `sessionId` visible to callers.
- `data-model.md` and `session-trace-lifecycle.md` describe the current model as `diagnosis_session + agent_step + tool_invocation`, and list run id as a known follow-up.
- `devflow/glossary/CONTEXT.md` now defines Chat Session, Diagnosis Run, and Diagnosis Trace. These terms must be used consistently in design/specs/tasks.
## Interface Impact
Level: L4 database/API contract migration with compatibility behavior.
- New API response field: `runId`.
- New query parameter: `GET /api/diagnosis/{sessionId}/trace?runId=...`.
- New API: `GET /api/chat/session/{sessionId}/runs`.
- Feedback request gains optional/preferred `runId`.
- Feedback response returns bound `runId` and `fallbackToLatestRun`.
- `/api/ai_ops` keeps SSE event name `message` and emits a `type=metadata` JSON message containing `sessionId` and `runId` before content.
- Database contract changes include new tables and new `run_id` columns.
- Old callers that only pass `sessionId` remain compatible by binding to latest run, but this fallback must be observable.
## Risks
- Historical data has no true per-round boundary; backfill can only create compatibility runs from existing `diagnosis_session` rows.
- Context propagation through Agent hooks and tools is easy to break because it currently combines `RunnableConfig.metadata` and `SessionContextHolder`.
- Evidence score, verifier inputs, and baseline metrics may change after run isolation because cross-round tool rows are no longer counted.
- Chat-only intermediate completion would leave AIOps as the remaining mixed-trace entry point; AIOps must be completed before overall archive.
- `case_library.diagnosis_id` becomes transitional: old data may contain `session_id`, new data contains `run_id`.