UAT: OutputSession.JsonMaterializer messages field is empty dict {} instead of spec-required list of {level, text} objects #4497

Open
opened 2026-04-08 13:50:19 +00:00 by HAL9000 · 0 comments
Owner

Bug Report

Feature Area: Output Rendering Framework — JSON Envelope Format
Severity: Medium
Discovered by: UAT tester (uat-worker-output-rendering-001)


Summary

When using OutputSession with JsonMaterializer, the messages field in the JSON envelope is always an empty dict {} instead of the spec-required list of {level, text} objects.

Expected Behavior (per spec)

The spec defines the JSON envelope as:

{
  "command": "project show",
  "status": "ok",
  "exit_code": 0,
  "data": { ... },
  "timing": { "duration_ms": 42 },
  "messages": [
    { "level": "ok", "text": "Environment loaded" }
  ]
}

messages must be a list of objects with level and text keys.

Actual Behavior

from cleveragents.cli.output import OutputSession, JsonMaterializer
import json

strategy = JsonMaterializer()
with OutputSession(format='json', command='info', strategy=strategy) as session:
    panel = session.panel('Test')
    panel.set_entry('Key', 'Value')

output = json.loads(strategy.get_output())
print(output['messages'])  # Output: {}  (empty dict!)

The messages field is always {} (empty dict) because StructuredOutput.metadata (which maps to messages in the serialized output) is initialized as an empty dict and never populated.

Root Cause

In src/cleveragents/cli/output/materializers.py, the _snapshot_to_dict function maps snapshot.metadata to messages:

def _snapshot_to_dict(snapshot: StructuredOutput) -> dict[str, Any]:
    result: dict[str, Any] = {
        "command": snapshot.command,
        "status": snapshot.status,
        "exit_code": snapshot.exit_code,
        "data": [_element_to_dict(e) for e in snapshot.elements],
        "timing": snapshot.timing,
        "messages": snapshot.metadata,  # ← This is always {} (empty dict)
    }
    return result

snapshot.metadata is a dict[str, Any] initialized to {} and never populated with status messages. The spec requires messages to be a list of {level, text} objects.

Note: The format_output() function in formatting.py correctly uses a list for messages, but OutputSession.JsonMaterializer does not.

Inconsistency

This creates an inconsistency between the two output paths:

Path messages type Correct?
format_output(data, 'json') list[{level, text}] ✓ Yes
OutputSession + JsonMaterializer {} (empty dict) ✗ No

Fix Required

The _snapshot_to_dict function should generate a proper messages list. When no explicit messages are provided, it should generate a default message from the session status:

def _snapshot_to_dict(snapshot: StructuredOutput) -> dict[str, Any]:
    # Generate messages list from metadata or default
    messages = snapshot.metadata.get('messages', [
        {"level": snapshot.status, "text": f"{snapshot.command} completed" if snapshot.command else "ok"}
    ])
    result: dict[str, Any] = {
        "command": snapshot.command,
        "status": snapshot.status,
        "exit_code": snapshot.exit_code,
        "data": [_element_to_dict(e) for e in snapshot.elements],
        "timing": snapshot.timing,
        "messages": messages,
    }
    return result

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: uat-tester

## Bug Report **Feature Area:** Output Rendering Framework — JSON Envelope Format **Severity:** Medium **Discovered by:** UAT tester (uat-worker-output-rendering-001) --- ## Summary When using `OutputSession` with `JsonMaterializer`, the `messages` field in the JSON envelope is always an empty dict `{}` instead of the spec-required list of `{level, text}` objects. ## Expected Behavior (per spec) The spec defines the JSON envelope as: ```json { "command": "project show", "status": "ok", "exit_code": 0, "data": { ... }, "timing": { "duration_ms": 42 }, "messages": [ { "level": "ok", "text": "Environment loaded" } ] } ``` `messages` must be a **list** of objects with `level` and `text` keys. ## Actual Behavior ```python from cleveragents.cli.output import OutputSession, JsonMaterializer import json strategy = JsonMaterializer() with OutputSession(format='json', command='info', strategy=strategy) as session: panel = session.panel('Test') panel.set_entry('Key', 'Value') output = json.loads(strategy.get_output()) print(output['messages']) # Output: {} (empty dict!) ``` The `messages` field is always `{}` (empty dict) because `StructuredOutput.metadata` (which maps to `messages` in the serialized output) is initialized as an empty dict and never populated. ## Root Cause In `src/cleveragents/cli/output/materializers.py`, the `_snapshot_to_dict` function maps `snapshot.metadata` to `messages`: ```python def _snapshot_to_dict(snapshot: StructuredOutput) -> dict[str, Any]: result: dict[str, Any] = { "command": snapshot.command, "status": snapshot.status, "exit_code": snapshot.exit_code, "data": [_element_to_dict(e) for e in snapshot.elements], "timing": snapshot.timing, "messages": snapshot.metadata, # ← This is always {} (empty dict) } return result ``` `snapshot.metadata` is a `dict[str, Any]` initialized to `{}` and never populated with status messages. The spec requires `messages` to be a list of `{level, text}` objects. Note: The `format_output()` function in `formatting.py` correctly uses a list for `messages`, but `OutputSession.JsonMaterializer` does not. ## Inconsistency This creates an inconsistency between the two output paths: | Path | `messages` type | Correct? | |------|----------------|---------| | `format_output(data, 'json')` | `list[{level, text}]` | ✓ Yes | | `OutputSession` + `JsonMaterializer` | `{}` (empty dict) | ✗ No | ## Fix Required The `_snapshot_to_dict` function should generate a proper `messages` list. When no explicit messages are provided, it should generate a default message from the session status: ```python def _snapshot_to_dict(snapshot: StructuredOutput) -> dict[str, Any]: # Generate messages list from metadata or default messages = snapshot.metadata.get('messages', [ {"level": snapshot.status, "text": f"{snapshot.command} completed" if snapshot.command else "ok"} ]) result: dict[str, Any] = { "command": snapshot.command, "status": snapshot.status, "exit_code": snapshot.exit_code, "data": [_element_to_dict(e) for e in snapshot.elements], "timing": snapshot.timing, "messages": messages, } return result ``` --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.5.0 milestone 2026-04-08 17:42:07 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#4497
No description provided.