Skip to content

[Bug]: otel-mapping.json requires acs.capability on gen_ai.tool.call, but capability is optional in tool-call-request.json #227

Closed

Description

@rocklambros

What is broken

otel-mapping.json makes acs.capability a required attribute on the gen_ai.tool.call span, and the hook payload it is derived from makes capability optional. An ACS-Trace emitter cannot populate a required attribute from a field the request may not carry.

Where

specification/v0.1.0/trace/otel-mapping.json, step_to_span["steps/toolCallRequest"].required_attributes
specification/v0.1.0/hooks/tool-call-request.json, properties.capability (not in required, which lists only tool and arguments)
docs/spec/trace/events.md, the steps/toolCallRequest row

What the specification says, and what happens instead

The mapping's description reads: "v0.1.0 conformance requires that a deployment emitting OTel for the Trace pillar uses these span names and required attributes verbatim." Its steps/toolCallRequest entry lists required_attributes ["gen_ai.tool.name", "acs.capability"]. The hook schema describes capability as an "Abstract capability identifier (e.g., filesystem.delete, network.egress)" and leaves it out of required. A conformant Observed Agent can send toolCallRequest with no capability. A conformant ACS-Trace emitter then has nothing to put in acs.capability. It either omits a required attribute, which fails the mapping, or invents one, which defeats the attribute.

Impact on implementers

The OTel exporter in #91 hits this on the first hook the reference evaluates live. A Trace conformance check that asserts required attributes fails against spec-conformant traffic. Verifiers reading the trace cannot tell a missing capability from an unclassified tool.

Two directions close it. Move acs.capability to optional_attributes in the mapping and state it is populated when the payload carries capability, which is additive and can land now. Or make capability required in tool-call-request.json, which is a wire change and belongs in v0.2.0.

Found while scoping the OTel export for #91.

Current Priority Scope

Feeds the runnable Guardian reference implementation

Activity

  1. added
    status:acceptedIn the backlog. Maintainers only
    scope:in-focusFeeds the ninety-day committed outcome. Maintainers only
    workstream:specOwning workstream. Maintainers only
    and removed
    status:needs-triageNot yet triaged. Applied by the issue forms
    on Oct 5, 2026
  2. self-assigned this
    on Oct 5, 2026
  3. MKaptein commented on Oct 8, 2026

    @MKaptein

    For v0.1 I would fully support a relaxed mapping: the exporter cannot emit a value the request does not carry so it's the only pragmatic workable method.

    However, for v0.2.0 I would personally prefer your second direction, making this information required. As you write, a verifier reading the trace cannot tell a missing capability from an unclassified tool, and the same holds for a Guardian. I wrote up a related idea in #232.

    I (and admittedly, an agent of mine) also went through the other hooks in otel-mapping.json and it seems like the same pattern exists in more places: the mapping requires an attribute whose source field is optional:

    Hook Required in the mapping In the hook schema
    steps/memoryStore acs.memory_store.name, acs.operation memory_store and operation are optional (only content is required)
    steps/memoryContextRetrieval acs.memory_store.name memory_store is optional (only results is required)
    steps/knowledgeRetrieval acs.source.type source is optional (only query and results are required)
    steps/subagentStop acs.subagent.final_chain_hash final_chain_hash is described as OPTIONAL

    (The above is at commit 770f1e0)

    The same rows are in docs/spec/trace/events.md. Since the exporter in #91 reads the mapping at runtime, it may be easiest to fix all these in a single pass.

    One small additional suggestion. You propose to move acs.capability to optional_attributes and state that it is populated when the payload carries capability. The mapping already has a list with that meaning, conditional_attributes, but only on the decision event, not per hook. If the per-hook entries get the same list, the rule would be in the JSON itself and an exporter could not skip the attribute when the request has it. The four attributes above could go there too.

  4. bar-capsule commented on Oct 8, 2026

    @bar-capsule
    Collaborator

    @rocklambros @MKaptein agree with both of you. For v0.1 let's relax the mapping, and I like Maurits's idea of a per-hook conditional_attributes list (filled whenever the payload has the field), so the rule lives in the JSON itself. I checked Maurits's rows against the schemas and they're all correct, so let's fix them in the same PR.

    I went through the rest of the mapping and found three more of the same kind:

    • decision event: acs.evaluator is required, but in response-envelope.json evaluator sits under metadata, which is optional. It can move to the decision event's conditional_attributes, next to acs.reasoning and acs.confidence.
    • agbom/changed: acs.agbom.change_reason is required, but reason is optional in agbom-changed.json.
    • agbom/snapshot and agbom/changed: acs.agbom.format is required, but no AgBOM payload has a format field (the wire form is always canonical), so the mapping needs to say where its value comes from.

    Two optional attributes also point at fields that don't exist: acs.provenance.lineage_depth (the mapping says it's on the wire, but provenance.json has no depth field) and acs.provenance.origins_compacted (post-compact.json has no origins field).

    The v0.2 question of making capability required on the wire can continue in #232.

  5. rocklambros commented on Oct 12, 2026

    @rocklambros
    CollaboratorAuthor

    Notes from reviewing #244, the OpenTelemetry exporter that reads this mapping at runtime. They are for the PR that closes this issue.

    lineage_depth. #244 asked whether to drop it or define it (its Q3). The answer there is that we drop it here, which matches Bar's list above. It appears in five spots: otel-mapping.json line 121 (the description says it is "on the wire") and line 131, ocsf-mapping.json:78, events.md:41, and extend_opentelemetry.md:20. acs.provenance.origins_compacted goes out in the same pass.

    Per-hook conditional_attributes needs a matching change in the exporter. #244's otel-trace-exporter.ts reads only required_attributes and optional_attributes from each step_to_span entry. Once acs.capability moves to a new conditional_attributes list, the exporter stops emitting it, and nothing fails. I've asked on #244 for the exporter to read all three lists. Whichever PR lands second has to make sure that happens.

    The conformance checker. packages/conformance/src/trace-pillar.ts walks only required_attributes for step spans (line 381), so moving an attribute out of that list drops it from the checker's rows. The PR should decide whether the checker reads conditional_attributes too. It should also touch something under reference-implementations/, because the reference implementation CI job only runs on that path, and a spec-only PR would skip it.

    acs.evaluator moving to the decision event's conditional list. #244 currently fills in deterministic when the response has no evaluator, because the mapping requires it. After this change, plus #229 putting metadata.evaluator on the wire, that fallback can go.

  6. MKaptein commented on Oct 13, 2026

    @MKaptein

    Thanks @rocklambros, I went through #261 and it seems to cover all the rows above. This all looks good to me, and the "MUST omit when absent" addition makes sense.

    Impressive speed on moving these things forward!

  7. added a commit that references this issue on Oct 14, 2026
    e5d4664
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

priority:P1Maintainers onlyscope:in-focusFeeds the ninety-day committed outcome. Maintainers onlystatus:acceptedIn the backlog. Maintainers onlytype:bugSomething isn't workingworkstream:specOwning workstream. Maintainers only

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions