Repository navigation
[BUG] APM 0.33.0: apm update fails to integrate Azure Skills because frozen hook metadata is not JSON serializable #3167
Description
With APM CLI 0.33.0, apm update fails while integrating microsoft/azure-skills:
[x] 1 package failed:
+- microsoft/azure-skills -- Failed to integrate primitives: Object of type mappingproxy is not JSON serializable
[x] Installation failed with 1 error(s).
Environment
- APM CLI: 0.33.0
- Reported installation: Windows, WinGet standalone executable and macOS 27.01 (Homebrew)
- Dependency: microsoft/azure-skills
- Targets: claude, codex, copilot
Steps to reproduce
Use APM CLI 0.33.0.
Add microsoft/azure-skills to an APM project with the following configuration.
Run apm update.
name: apm-hook-repro
version: 1.0.0
description: Minimal Azure Skills integration reproduction
author: APM user
targets:
- claude
- codex
- copilot
dependencies:
apm:
- microsoft/azure-skillsThe dependency is not pinned here; reproduction depends on it containing hooks with nested metadata, such as an env object.
Expected behavior
APM integrates the hooks successfully, preserving their environment variables
and other metadata in the generated native JSON documents.
Actual behavior
Integration fails with the error above, and the install transaction is not committed.
Root cause
src/apm_cli/hook_contract.py recursively freezes hook metadata in _freeze:
dictionaries become MappingProxyType instances, and lists become tuples.
This is appropriate for the immutable intermediate representation (IR).
However, src/apm_cli/integration/hook_native_formats.py only makes a shallow
copy with dict(handler.metadata) and dict(binding.metadata). Nested mappings
remain frozen. The raw-entry paths also return frozen metadata directly.
Consequently, JSON serialization fails when it encounters a nested mapping proxy.
The failure can be reproduced without downloading Azure Skills or executing hooks:
import json
from apm_cli.integration.hook_native_formats import _to_claude_hook_entries
entries = [{
"bash": "echo test",
"env": {"APM_TEST": "value"},
}]
json.dumps(_to_claude_hook_entries(entries))Proposed fix
Recursively materialize metadata at the native rendering boundary:
- Convert
Mappingvalues to new dictionaries. - Convert lists and tuples to new lists, recursively converting their elements.
- Preserve scalar values.
- Apply the conversion to handler metadata, binding metadata, and raw entries in
both the nested and flat rendering paths.
Keep the IR immutable. Do not stringify unsupported values with default=str,
discard env, or change dependency declarations as a workaround.
Regression coverage
- JSON round trips for Claude, Codex, Gemini, and nested/flat Antigravity adapters.
- Preservation of nested
env, extension metadata, arrays, and scalar values. - Preservation of commands, timeout conversion, matchers, and provenance.
- Raw-entry round trips, including mappings inside arrays.
- Mutation of rendered output does not mutate the source or frozen IR.
- Gemini's in-place compatibility conversion preserves
env.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
danielmeppiel commentedon Oct 7, 2026
Decision: approve
Area: project
Scope: Restore lossless native JSON rendering of supported hook metadata during install/update while retaining the immutable hook IR and existing supported adapter behavior.
Done when: Handler, binding and raw-entry paths preserve nested mappings, arrays and scalar values as independent JSON-compatible output; supported Claude, Codex, Gemini and nested/flat Antigravity rendering retains commands, matchers, timeout conversion and provenance; mutating rendered output cannot mutate source metadata or the frozen IR; no default=str, metadata dropping or consent bypass is used; a pinned or local CLI install/update reproduction covers the reported failure without executing hooks; and directly related guidance reflects the repair.
Out of scope: New hook schemas, event translation, targets, trust/consent models or runtime execution; implementation dispatch from this decision session; restarting blocked delivery; PR review or merge approval; release commitments.
Review contact: Daniel Meppiel (@danielmeppiel)
Reason: Recorded by Copilot after Daniel Meppiel (@danielmeppiel) explicitly approved the bounded proposal and reviewed the installation-failure explanation. This is a human-confirmed product-scope decision, not an automated triage recommendation or authorization to start an automation delivery run.
abhishekacharya200 commentedon Oct 12, 2026
Opened #3211 for this approved scope. It thaws frozen hook metadata at the native rendering boundary and keeps the IR immutable.
fix(hooks): render nested hook metadata as plain JSON
test(hooks): cover nested metadata through native file writes
test(hooks): consolidate metadata provenance and CLI regressions
fix(hooks): render nested hook metadata as plain JSON (#3211)