microsoft / microsoft/amplifier

[app-cli + foundation] Sub-agent session IDs always have an all-zero parent span, destroying delegate→parent attribution

Open
#348 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
3.1k
Forks
261
Avg merge
3h 28m
Merged PRs (30d)
13

Description

Summary

Every sub-agent session ID has an all-zero parent span: 0000000000000000-<child>_<agent>. The {parent-span}-{child-span} format from amplifier_foundation.tracing never carries real lineage, so tooling that reads session directory names cannot attribute a delegate session to its parent. On one machine, 1394 of 1394 sub-session directories are zeroed — not a single counter-example.

This is two bugs that mask each other; fixing either alone leaves the behavior unchanged.

Bug A: parent_trace_id is always None

amplifier_app_cli/session_spawner.py:405-410:

sub_session_id = generate_sub_session_id(
    agent_name=agent_name,
    parent_session_id=parent_session.session_id,
    parent_trace_id=getattr(parent_session, "trace_id", None),
)

AmplifierSession (amplifier_core/session.py:24) has no trace_id attribute — grep -rn trace_id amplifier_core/ returns nothing. The getattr therefore always yields None.

In amplifier_foundation/tracing.py:generate_sub_session_id, both ways of obtaining a parent span then fail:

  • Lines 89-94 match parent_session_id against _SPAN_PATTERN = ^([0-9a-f]{16})-([0-9a-f]{16})_. Root session IDs are dashed uuid4 (amplifier_core/session.py:71), e.g. 071a2b27-7562-4242-8ea6-8911809a5a28: 8 hex in the first group, no underscore. No match.
  • Lines 98-104 (the trace-ID fallback) are gated on parent_trace_id being truthy, so they never run.
  • Line 87's parent_span = _DEFAULT_PARENT_SPAN ("0" * 16) survives.

amplifier_module_tool_delegate/__init__.py:993 has the same hole independently: it passes parent_session_id only, never a trace ID.

Bug B: the stored trace_id cannot satisfy _TRACE_ID_PATTERN

session_spawner.py:800 performs the same lookup with a different default, and persists the result as metadata.json's trace_id (line 812):

parent_trace_id = getattr(parent_session, "trace_id", parent_session.session_id)

Since the fallback is session_id, every stored trace ID is a dashed 36-char UUID, while _TRACE_ID_PATTERN is ^[0-9a-f]{32}$ (tracing.py:24). Measured across 1355 metadata.json files: 0 valid 32-hex trace IDs. So aligning line 409's default with line 800's is not sufficient — the UUID must be normalized to hex before it reaches generate_sub_session_id.

Why it is unconditional

The line 89-94 path can only succeed if the parent is itself a sub-session, which requires nested delegation. tool-delegate defaults exclude_tools = ["tool-delegate"] (amplifier_module_tool_delegate/__init__.py:129), so spawned agents do not inherit the delegate tool and nesting effectively never occurs. Result: the parent component is dead weight in 100% of real sessions.

Repro

from amplifier_foundation.tracing import generate_sub_session_id as g
root = "071a2b27-7562-4242-8ea6-8911809a5a28"  # a real root session id

# 1. what session_spawner.py:409 does today
g(agent_name="explorer", parent_session_id=root)
# -> '0000000000000000-2ed57072cf184b17_explorer'   BUG

# 2. passing the dashed uuid that metadata.json stores as trace_id
g(agent_name="explorer", parent_session_id=root, parent_trace_id=root)
# -> '0000000000000000-68ba3ed2d49b44c4_explorer'   STILL BUGGED (Bug B)

# 3. passing the uuid hex with dashes stripped
g(agent_name="explorer", parent_session_id=root, parent_trace_id=root.replace('-', ''))
# -> '756242428ea68911-85d3baf91ef2496a_explorer'   correct

Or on any existing install:

find ~/.amplifier/projects -mindepth 3 -maxdepth 3 -type d -path '*/sessions/*' \
  | sed 's|.*/sessions/||' | grep -E '^[0-9a-f]{16}-[0-9a-f]{16}_' \
  | grep -vc '^0000000000000000-'
# -> 0

Suggested fix

Normalize the root UUID into a 32-hex trace ID once, at session creation, and propagate it:

  1. Give AmplifierSession a real trace_id attribute, defaulting to uuid.UUID(self.session_id).hex for root sessions and inherited verbatim by children — so the two getattr sites can no longer disagree.
  2. Failing that, have both session_spawner.py:409 and :800 use one shared helper that strips dashes: parent_session.session_id.replace("-", "").
  3. Optionally relax _TRACE_ID_PATTERN in tracing.py:24 to accept a dashed UUID and normalize internally, making the API forgiving of the natural input.
  4. Pass a trace ID from tool-delegate/__init__.py:993 too, so the foundation-internal path is fixed independently of app-cli.

Existing sessions remain attributable — parent_id is already recorded in both metadata.json and the session:fork event — so a fix needs no data migration, only forward correctness.

Related

#314 (session IDs truncated in session list --format json — same family, different path), #252 (closed; parent_session_id persisted for sub-recipes).

Versions: amplifier-app-cli 0.1.1 (a0c13cd1aa4b1bf9e3e155d4e12b306a87baa09a), amplifier-foundation 1.0.0 (0d5c5204befc33febfc2bdb8cd8d72ff0a3e6c84), amplifier-core 1.6.0. macOS 26 (Darwin 25.5.0), Python 3.12.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the three generate_sub_session_id cases from the issue, then read amplifier_app_cli/session_spawner.py, amplifier_foundation/tracing.py, amplifier_core/session.py, and amplifier_module_tool_delegate/init.py at the cited locations. Verify that root UUIDs become valid 32-hex trace IDs and that both spawning paths produce non-zero parent spans; existing metadata and session:fork attribution should remain compatible.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.