Skip to content

[AISOS-2158] Improve Langfuse span labels for artifact generation and revisions#148

Open
ekuris-redhat wants to merge 9 commits into
forge-sdlc:mainfrom
ekuris-redhat:forge/aisos-2158
Open

[AISOS-2158] Improve Langfuse span labels for artifact generation and revisions#148
ekuris-redhat wants to merge 9 commits into
forge-sdlc:mainfrom
ekuris-redhat:forge/aisos-2158

Conversation

@ekuris-redhat

@ekuris-redhat ekuris-redhat commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

This pull request improves telemetry accuracy by refactoring the Langfuse span labeling logic to support highly structured workflow tagging. By replacing flat, ambiguous labels with structured classifications, it ensures that token costs, latencies, and generation metrics are correctly attributed to their respective stages rather than leaking into incorrect stages like spec approval gates. This enables clear, reliable dashboard visualization in Grafana to distinguish initial artifact generation, revisions, downstream breakdowns, and approval gates.

Changes

Core Integrations & Telemetry

  • Modified src/forge/integrations/langfuse/fields.py to structure the workflow_step tracing field value using a precise [workflow_stage_or_node]:[operation_type]:[artifact_type](:attempt-[retry_count]) pattern.
  • Updated _resolve_workflow_step logic to accurately parse and classify breakdown steps (e.g., decompose_epics, generate_tasks), approval gates, revisions, and clarification or Q&A flows.
  • Added is_question, is_revision, revision_requested, and task to _TRACE_FIELD_KEYS in src/forge/integrations/agents/agent.py to ensure trace fields are preserved and forwarded in the tracing context.
  • Refactored code structure to clean up formatting and satisfy style guides, resolving linter warnings (Ruff SIM114).
  • Removed the unused vestigial Pipfile from the root.

Test Suite Updates

  • Expanded comprehensive unit tests in tests/unit/integrations/langfuse/test_fields.py.
  • Added test coverage for new workflow step label patterns including breakdowns, Q&As, revisions, and approval gates to ensure stability and prevent regressions.

Implementation Notes

  • Tagging Format: The pattern chosen ([workflow_stage_or_node]:[operation_type]:[artifact_type]) maintains backward compatibility while introducing hierarchical dimensions suitable for robust Grafana dashboard aggregation and filtering.
  • Edge-Case Parsing: Special care was taken to properly isolate downstream breakdown steps like decompose_epics to prevent them from being misclassified as upstream spec approval gates.

Testing

  • Expanded and ran comprehensive unit tests within tests/unit/integrations/langfuse/test_fields.py to cover all new tagging permutations.
  • Verified all system unit and integration tests successfully via Pytest, confirming that telemetry changes did not impact core agent flows or execution.

Related Tickets


Generated by Forge SDLC Orchestrator

… revisions

Detailed description:
- Updated _resolve_workflow_step in src/forge/integrations/langfuse/fields.py to return a structured tag.
- Parsed stage/node name, operation type, artifact type, and attempt count.
- Prevented epic breakdowns from being labeled as spec approval gates.
- Added comprehensive unit test coverage.

Closes: AISOS-2158
Auto-committed by Forge container fallback.
… Revisions

Detailed description:
- Refactored  in  to support structured tagging.
- Correctly maps stage/node, operation type (initial, revision, question, approval gate, breakdown), artifact type, and attempt context.
- Added corresponding tests in  and ran all unit/integration tests successfully.

Closes: AISOS-2158
Detailed description:
- Reformatted long line exceeding max length in src/forge/integrations/langfuse/fields.py to comply with standard formatting and satisfy the Ruff linter/formatter.

Closes: AISOS-2158-ci-fix

@ekuris-redhat ekuris-redhat left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The operation-type detection in _resolve_workflow_step reads is_question, is_revision, revision_requested, and task from the
state dict, but none of these keys are included in _TRACE_FIELD_KEYS (agent.py:61-75). The _forward_trace_fields function filters
them out before they reach the resolver, so at runtime they will always be None.

This means distinguishing questions from approval gates won't work in production — e.g. a question at spec_approval_gate will be
labeled approval_gate instead of question_asking. The tests pass because they inject these flags directly into the state dict,
bypassing the _forward_trace_fields filtering.

Either add is_question, is_revision, revision_requested, and task to _TRACE_FIELD_KEYS, or rework the detection to only use
fields that are actually forwarded.

Also please remove the Pipfile

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

Detailed description:
- Added 'is_question', 'is_revision', 'revision_requested', and 'task' to _TRACE_FIELD_KEYS in src/forge/integrations/agents/agent.py to ensure trace fields are preserved and forwarded in the tracing context.
- Removed the unused vestigial Pipfile from the root.

Closes: AISOS-2158-review-fix

@ekuris-redhat ekuris-redhat left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identity mapping for decompose_epics/generate_tasks in _resolve_workflow_step is dead code — both branches produce the same
result as stage_or_node = node_str. Please simplify by removing the if/else and just using stage_or_node = node_str.

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

@ekuris-redhat ekuris-redhat left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally on a feature workflow (AISOS-2160). The label logic in _resolve_workflow_step is
correct, but current_node in the trace context is stale — every node copies it from the incoming
state via state.get("current_node", ""), which still holds the previous gate's name.

Example: decompose_epics receives current_node = "plan_approval_gate", so the Langfuse trace shows
plan_approval_gate:approval_gate:plan instead of decompose_epics:breakdown:epic_breakdown.

Fix this in _forward_trace_fields in src/forge/integrations/agents/agent.py — resolve the actual
executing node name from the LangGraph runtime context instead of trusting the stale current_node
from the state dict.

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

@ekuris-redhat ekuris-redhat left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ensure_config() fix in _forward_trace_fields only covers wrapper methods (generate_prd,
generate_spec, generate_epics). But there's no generate_tasks wrapper — task_generation.py:308
calls run_task() directly with the stale current_node from state, and _forward_trace_fields is
never invoked.

The same ensure_config() override should be applied in run_task when building trace_state
(agent.py:775-779), so all call paths get the correct node name. Same issue applies to other direct
run_task callers like triage.py:78 and task_takeover_planning.py:129.

Verified locally on AISOS-2160: task generation traces still show
plan_approval_gate:approval_gate:plan.

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

@ekuris-redhat ekuris-redhat left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the pipfile as its not needed. Keep all the rest of the code as is.

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

@ekuris-redhat ekuris-redhat left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the pipfile as its not needed. Keep all the rest of the code as is.

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

# Determine artifact_type
# Map based on node name, task, or state properties
# Target artifacts: spec, prd, plan, tasks, epic_breakdown, etc.
artifact_type = "unknown"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q&A spans lose their artifact type on the real execution path. run_task() replaces current_node with LangGraph’s answer_question, so this resolver produces answer_question:question_asking:unknown, even though answer_question() includes artifact_type in the trace state. The unit test passes spec_approval_gate directly and therefore does not exercise that overwrite. Could we prefer the explicit state["artifact_type"] value before falling back to the node/task heuristics, and add an integration-level test for this path?

@ekuris-redhat

Copy link
Copy Markdown
Collaborator Author

Forge is addressing PR review feedback now. This status update is informational.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants