Skip to content

Add task_id to spans and fix Investigate Traces button#197

Merged
declan-scale merged 8 commits intomainfrom
declan-scale/investigate-traces-button
Apr 17, 2026
Merged

Add task_id to spans and fix Investigate Traces button#197
declan-scale merged 8 commits intomainfrom
declan-scale/investigate-traces-button

Conversation

@declan-scale
Copy link
Copy Markdown
Collaborator

@declan-scale declan-scale commented Apr 15, 2026

Summary

  • Add task_id column to spans: New nullable task_id foreign key on the spans table with a migration that backfills existing spans where trace_id matches a valid task ID. This lets the UI and API query spans by task directly instead of relying on the convention that trace_id equals the task ID.
  • Fix Investigate Traces button: The button now uses the actual trace_id from spans (fetched via the new task_id query) rather than assuming task_id == trace_id. This makes the SGP Monitor link work correctly for agents whose trace IDs differ from their task IDs.
  • Fix thinking indicator logic: The "Thinking..." indicator now correctly appears after tool calls complete (not just before the first agent response), and reasoning content no longer duplicates by concatenating both content and summary — it shows content when available, falling back to summary.

Backend changes

  • New Alembic migration adds task_id column to spans table with FK to tasks, backfills from trace_id, and adds index
  • SpanORM, SpanEntity, create/update schemas, and use case all support task_id
  • /spans list endpoint accepts task_id query parameter
  • Integration tests cover task_id on create, update, and null default

Frontend changes

  • useSpans hook queries by task_id first, falls back to trace_id for backward compat
  • InvestigateTracesButton accepts traceId instead of taskId
  • TaskHeader fetches spans and extracts the real trace_id for the button
  • Thinking indicator and reasoning dedup logic fixed

Resolves AGX1-188

Follow-up required

The agentex-python SDK needs to be updated after this lands to support the new task_id field on span creation/listing. Until the SDK is updated, agents will continue to work (the field is nullable), but won't be able to explicitly set task_id when creating spans.

Test plan

  • Verify spans can be created with and without task_id
  • Verify /spans?task_id=<id> returns the correct spans
  • Verify the Investigate Traces button links to the correct SGP Monitor trace
  • Verify thinking indicator appears at the right times (after tool calls, before text response)
  • Verify reasoning content doesn't show duplicated text
  • Run make test for backend integration tests

🤖 Generated with Claude Code

Greptile Summary

This PR adds a task_id FK column to the spans table, backfills it from trace_id, and threads the new field through the ORM, schemas, use-case, and API. The frontend is updated to query spans by task_id first (falling back to trace_id for backward compatibility), and the Investigate Traces button now uses the actual trace_id from fetched spans rather than assuming task_id == trace_id. Two additional UI fixes land alongside: reasoning content no longer concatenates content + summary, and the "Thinking..." indicator shows correctly after completed tool calls.

The FK constraint issue in the integration tests (flagged in the prior review thread) is addressed by introducing test_agent and test_tasks fixtures that create real DB rows before inserting spans.

Confidence Score: 4/5

Safe to merge with awareness of the outstanding P1 items from prior review threads (loading-state race on the Investigate Traces button, misleading batching comment in the migration).

No new P0/P1 issues found in this pass. The prior P1 threads (button shows wrong URL while spans load, misleading batching comment in migration) remain open; if those are accepted as-is the score would move to 5. All backend changes are consistent and well-tested.

agentex-ui/components/task-header/task-header.tsx (loading-state guard) and the migration file (batching comment) — both covered by prior threads.

Important Files Changed

Filename Overview
agentex/database/migrations/alembic/versions/2026_04_14_1126_add_task_id_to_spans_57c5ed4f59ae.py Migration adds task_id column, backfills, then adds FK and index. Column → backfill → FK order is correct, but the comment claims batching that the SQL doesn't implement (flagged in prior thread).
agentex/src/adapters/orm.py Adds task_id FK column and matching index to SpanORM. Correctly uses ondelete="SET NULL" and aligns with migration definition.
agentex/src/api/routes/spans.py Passes task_id through create and update routes; adds task_id as optional query parameter to list endpoint. Clean and consistent with existing patterns.
agentex/src/api/schemas/spans.py Adds optional task_id field to CreateSpanRequest and UpdateSpanRequest with appropriate Field metadata.
agentex/src/domain/entities/spans.py Adds nullable task_id field to SpanEntity. Correctly stores only the ID reference.
agentex/src/domain/use_cases/spans_use_case.py Threads task_id through create, partial_update, and list; replaces the single-filter pattern with a dict that supports both trace_id and task_id independently or combined.
agentex/tests/integration/api/spans/test_spans_api.py Adds test_agent and test_tasks fixtures to satisfy FK constraints; covers create/update/list filtering with task_id. The test_agent fixture uses a hardcoded name "spans-test-agent" — safe only because isolated_repositories provides per-function isolation.
agentex/tests/unit/repositories/test_span_repository.py Inserts a real TaskORM row before creating spans to satisfy the new FK constraint; adds a dedicated test verifying ON DELETE SET NULL behaviour.
agentex/tests/unit/repositories/test_task_repository.py Applies unique_suffix to all hardcoded agent/task names to prevent unique-constraint collisions in session-scoped shared DBs. No logic changes.
agentex-ui/components/task-header/investigate-traces-button.tsx Renames prop from taskId to traceId and uses it in the SGP Monitor URL. Straightforward and correct.
agentex-ui/components/task-header/task-header.tsx Calls useSpans to derive the real trace_id, but doesn't guard the button on isLoading — during the initial fetch, traceId falls back to taskId, so the button briefly navigates to the wrong URL (flagged in prior thread).
agentex-ui/components/task-messages/task-message-reasoning-content.tsx Fixes reasoning dedup: shows content when non-empty, falls back to summary, instead of concatenating both arrays.
agentex-ui/components/task-messages/task-messages.tsx Refactors shouldShowThinkingForLastPair to correctly surface "Thinking..." after completed tool/reasoning/data messages, and hides it while tools are in-flight or text is streaming. Logic is well-commented and handles all content types.
agentex-ui/hooks/use-spans.ts Queries by task_id first then falls back to trace_id for backward compat. Single cache key covers both requests, but the unconditional fallback fires a second request on every poll for zero-span tasks (flagged in prior thread).

Sequence Diagram

sequenceDiagram
    participant TH as TaskHeader
    participant TS as TracesSidebar
    participant US as useSpans hook
    participant API as /spans API
    participant DB as PostgreSQL

    TH->>US: useSpans(taskId)
    TS->>US: useSpans(taskId)
    Note over US: React Query deduplicates same queryKey
    US->>API: GET /spans?task_id={taskId}
    API->>DB: SELECT * FROM spans WHERE task_id = ?
    DB-->>API: spans[]
    API-->>US: spans (non-empty)
    US-->>TH: spans[0].trace_id → traceId
    US-->>TS: spans for display

    Note over US: If task_id query returns empty...
    US->>API: GET /spans?trace_id={taskId}
    API->>DB: SELECT * FROM spans WHERE trace_id = ?
    DB-->>API: spans[] (legacy)
    API-->>US: spans
    US-->>TH: spans[0].trace_id → traceId
    US-->>TS: spans for display

    TH->>TH: render InvestigateTracesButton(traceId)
Loading

Comments Outside Diff (1)

  1. agentex/src/adapters/orm.py, line 246 (link)

    P1 FK missing ON DELETE SET NULL

    The new foreign key has no ON DELETE clause, so PostgreSQL defaults to NO ACTION (effectively RESTRICT). Any attempt to delete a task that has spans referencing it via task_id will raise a FK violation error. The migration backfills existing spans, so production databases may already have many tasks linked to spans after the upgrade.

    If task deletion is ever exercised — through a cleanup job, the API, or even test teardown — this constraint will block it silently.

    Adding ON DELETE SET NULL is the right semantic choice for a nullable FK: the span survives and simply loses its task_id association.

    And the matching migration should also specify:

    op.create_foreign_key(
        'fk_spans_task_id_tasks',
        'spans', 'tasks',
        ['task_id'], ['id'],
        ondelete='SET NULL',
    )
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: agentex/src/adapters/orm.py
    Line: 246
    
    Comment:
    **FK missing `ON DELETE SET NULL`**
    
    The new foreign key has no `ON DELETE` clause, so PostgreSQL defaults to `NO ACTION` (effectively `RESTRICT`). Any attempt to delete a task that has spans referencing it via `task_id` will raise a FK violation error. The migration backfills existing spans, so production databases may already have many tasks linked to spans after the upgrade.
    
    If task deletion is ever exercised — through a cleanup job, the API, or even test teardown — this constraint will block it silently.
    
    Adding `ON DELETE SET NULL` is the right semantic choice for a nullable FK: the span survives and simply loses its `task_id` association.
    
    
    
    And the matching migration should also specify:
    ```python
    op.create_foreign_key(
        'fk_spans_task_id_tasks',
        'spans', 'tasks',
        ['task_id'], ['id'],
        ondelete='SET NULL',
    )
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Cursor Fix in Claude Code

Reviews (5): Last reviewed commit: "Allow delete -> null" | Re-trigger Greptile

@declan-scale declan-scale requested a review from a team as a code owner April 15, 2026 19:53
Comment thread agentex/tests/integration/api/spans/test_spans_api.py
Comment thread agentex-ui/components/task-header/task-header.tsx
Comment thread agentex-ui/hooks/use-spans.ts
@declan-scale declan-scale force-pushed the declan-scale/investigate-traces-button branch from 6a84b54 to 1e16dad Compare April 15, 2026 20:01
@declan-scale declan-scale merged commit a05d211 into main Apr 17, 2026
29 checks passed
@declan-scale declan-scale deleted the declan-scale/investigate-traces-button branch April 17, 2026 15:30
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.

2 participants