feat(runner): add workflow context to Langfuse traces#726
feat(runner): add workflow context to Langfuse traces#726
Conversation
The operator already sets ACTIVE_WORKFLOW_GIT_URL, ACTIVE_WORKFLOW_BRANCH,
and ACTIVE_WORKFLOW_PATH env vars on runner pods, but the observability
layer never read them. This adds workflow_url, workflow_branch, and
workflow_path parameters to ObservabilityManager.initialize() and wires
them from ClaudeBridge._setup_observability().
Langfuse traces now include:
- metadata: workflow_name, workflow_url, workflow_branch, workflow_path
- tags: workflow:{derived_name}
This enables filtering and grouping traces by which concrete workflow
was invoked.
Made-with: Cursor
This comment has been minimized.
This comment has been minimized.
Covers the new workflow metadata and tag propagation: - Full workflow URL/branch/path/derived name in metadata and tags - No workflow fields when workflow_url is empty or whitespace - Name derivation: strips .git suffix, handles trailing slashes - Optional fields (branch, path) omitted when empty Made-with: Cursor
Claude Code ReviewSummaryPR #726 adds workflow context to Langfuse traces by extending Issues by SeverityBlocker IssuesNone. Critical Issues1. Workflow fields written to Langfuse without sanitization, inconsistent with The existing code explicitly sanitizes the model name before metadata/tag storage (observability.py:257-260): # SECURITY: Model name is sanitized via sanitize_model_name() to prevent injection attacks.
# Only alphanumeric chars and allowed separators (-, _, :, @, ., /) are permitted.
# This prevents malicious tag values from disrupting Langfuse API or metadata storage.The new workflow fields skip this step (observability.py:278-288):
The values originate from operator-set env vars (limited attack surface), but the existing security comment targets Langfuse API/metadata disruption — that concern applies equally here. A repo named with special characters would produce an unsanitized tag. Recommendation: Apply Major Issues2. URL-to-name derivation logic is duplicated bridge.py:223 (existing get_mcp_status): workflow_name = active_workflow_url.split("/")[-1].removesuffix(".git")observability.py:278 (new code, more correct): derived_name = workflow_url.rstrip("/").split("/")[-1].removesuffix(".git").strip()Both exist independently and can diverge. Recommend extracting a shared Minor Issues3. Test boilerplate repeated across all 6 new test cases Every test in 4. Missing edge-case test: empty The code has 5.
Positive Highlights
Recommendations
Reviewed by Claude Code (claude-sonnet-4-6) 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
…cement Address review feedback from PR #726: 1. Sanitize derived_name through sanitize_model_name() before using it in the Langfuse tag, consistent with how model names are handled. 2. Sanitize workflow_url, workflow_branch, and workflow_path through validate_and_sanitize_for_logging() to strip control characters and prevent log injection. 3. Move log message inside the if-block so it only claims a tag was added when derived_name is non-empty; log a different message for the fallback case. 4. Add tests for empty derived_name edge case and control character sanitization. Made-with: Cursor
Claude Code ReviewSummaryThis PR wires the three The implementation follows existing patterns well, applies proper sanitization throughout, and handles all the key edge cases. The iterative commit history shows good responsiveness to review feedback. Issues by SeverityBlocker IssuesNone. Critical IssuesNone. Major Issues1. The 3-line diff in Minor Issues1.
2. Workflow metadata fields have no explicit length cap
3. Mock setup boilerplate repeated across 8 test methods Every test in 4. SSH-style URLs stored as-is in
Positive Highlights
Recommendations
Overall: clean, low-risk feature addition that follows the project's patterns. The Major gap (missing Review generated by Claude Code -- claude-sonnet-4-6 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
Tests ClaudeBridge._setup_observability() to verify the three ACTIVE_WORKFLOW_* env vars are read from RunnerContext and forwarded to ObservabilityManager.initialize(). Covers both the populated and absent cases. Made-with: Cursor
Claude Code ReviewSummaryPR #726 adds workflow context ( Verdict: Approved with minor notes. This is a clean, well-tested observability enhancement. Issues by Severity🚫 Blocker IssuesNone. 🔴 Critical IssuesNone. 🟡 Major Issues1. Credential leakage risk in
parsed = urlparse(workflow_url)
safe_url = urlunparse(parsed._replace(netloc=parsed.hostname or ""))
metadata["workflow_url"] = validate_and_sanitize_for_logging(safe_url)2.
# sanitize_model_name() is reused here: its allowed charset is a superset
# of valid git repo-name characters, so it works as a tag sanitizer.
derived_name = sanitize_model_name(raw_name) or ""🔵 Minor Issues3. Test setup boilerplate repeated seven times ( The 4. The existing function signature uses 5. Test partial-match assertion ( assert "Workflow 'bug-fix-workflow' added to session metadata" in caplog.textThis is a substring match that happens to pass but doesn't verify "and tags". Low risk — just worth noting if the log message ever changes. Positive Highlights
Recommendations
🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
Customers want to be able to get data about which workflows are being used the most. This PR adds support for tracking which workflows are being used, and emits data into Langfuse.
Summary
workflow_url,workflow_branch, andworkflow_pathparameters toObservabilityManager.initialize()inobservability.pyACTIVE_WORKFLOW_*env vars (already set by the operator on runner pods) throughClaudeBridge._setup_observability()into Langfuse trace metadata and tagsworkflow:{name}tag for easy filtering in the Langfuse UIMotivation
The operator already propagates
spec.activeWorkflowfrom the AgenticSession CR into env vars on runner pods, andworkspace.pyreads them to set up the working directory. However, the observability layer never consumed these values, so Langfuse traces could not be filtered or grouped by workflow.Changes
ambient_runner/observability.pyinitialize()signature; added workflow metadata + tagambient_runner/bridges/claude/bridge.pyACTIVE_WORKFLOW_*env vars and passes toobs.initialize()Result
Langfuse traces now include:
{ ..., workflow_name, workflow_url, workflow_branch, workflow_path }["...", "workflow:bug-fix-workflow"]Test plan
ACTIVE_WORKFLOW_GIT_URLis set.gitsuffix and trailing slashesMade with Cursor