Skip to content

fix(audit): ignore hook sidecar object-key order - #3155

Open
sama Pyb (Pybsama) wants to merge 3 commits into
microsoft:mainfrom
Pybsama:codex/fix-claude-hook-drift-key-order
Open

sama Pyb (Pybsama) wants to merge 3 commits into
microsoft:mainfrom
Pybsama:codex/fix-claude-hook-drift-key-order

Conversation

@Pybsama

@Pybsama sama Pyb (Pybsama) commented Oct 3, 2026 •

Copy link
Copy Markdown

Description

A clean install can fail apm audit --ci when a consumer already has Claude hook events: the live merge and the empty scratch replay write ownership sidecar keys in different orders. Compare registered hook sidecars as UTF-8 JSON, preserving every field and list order while ignoring object-key order and formatting. Invalid encoding, malformed JSON, duplicate keys and non-JSON constants (NaN, Infinity, -Infinity) still produce drift findings. The existing ownership guard protects this comparison and its drift call sites; bundled CLI guidance matches the public documentation.

Shared native hook configurations continue to compare only APM-owned entries. Lockfile hashing and ownership decisions are unchanged.

Issue and approved scope

Fixes #3062.

Human scope approval: #3062 (comment)

This covers the approved false-positive repair and preserves meaningful drift detection.

Type of change

  • Bug fix
  • Documentation

Testing

  • Tested locally
  • Added regression tests

The hermetic install/install/audit regression fails against the original source and passes with this change. It checks stable repeated installation, preservation of user hooks and read-only audit. Unit cases cover key reordering, changed commands/value types, entry removal, added events, execution order, duplicate keys, malformed JSON and unsupported UTF-16/32 encodings.

uv run pytest tests/unit/install/test_drift.py tests/integration/test_drift_check.py tests/integration/test_hook_sidecar_drift_e2e.py tests/unit/integration/test_hook_integrator.py tests/unit/integration/test_hook_integrator_defect_regression.py tests/unit/install/test_drift_phase3.py tests/integration/test_drift_check_e2e.py -q: 364 passed. Four architecture mutation regressions also pass; they cover missing comparison authority, duplicate ownership, and bypassed drift call sites.

The six CI test-quality/architecture-ratchet modules also pass locally: 91 passed. Full-source Ruff lint/format and pylint duplication checks, YAML I/O/file-length/portable-path guards, auth and architecture boundary checks, and git diff --check pass. The full functional suite and Windows runtime were not run locally; these are local checks, not a claim that GitHub CI has run.

Spec conformance (OpenAPM v0.1)

No normative requirement changes: sidecars are outside the deployed-file hash maps governed by req-lk-012/017, and this change does not alter the ownership/reconciliation obligations in req-lk-021.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Nonstandard JSON constants remain accepted, and required architecture and bundled-guide updates are missing.

Review effort: Balanced
Findings: 2 Medium severity · 1 Low severity

Open (3)
What changed in this PR

Fixes false drift findings caused by JSON object-key ordering in hook ownership sidecars.

Changes:

  • Canonicalizes sidecar JSON while preserving values and list order.
  • Adds unit and end-to-end regression coverage.
  • Updates audit documentation.
File Description
src/​apm_cli/​integration/​hook_ownership.py Adds sidecar canonicalization.
src/​apm_cli/​install/​drift.py Uses canonical comparison for sidecars.
src/​apm_cli/​install/​manifest_reconcile.py Updates reconciliation documentation.
tests/​unit/​install/​test_drift.py Covers sidecar comparison cases.
tests/​integration/​test_hook_sidecar_drift_e2e.py Tests install and audit lifecycle.
docs/​src/​content/​docs/​reference/​cli/​audit.md Documents JSON comparison behavior.
docs/​src/​content/​docs/​reference/​baseline-checks.md Updates drift-check semantics.
docs/​src/​content/​docs/​enterprise/​drift-detection.md Updates enterprise guidance.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

return result


def canonicalize_hook_sidecar(sidecar_bytes: bytes) -> bytes:
"""
# The integrator reads sidecars as UTF-8; json.loads(bytes) would also
# accept UTF-16/32 files that the next install cannot read.
sidecar = json.loads(sidecar_bytes.decode("utf-8"), object_pairs_hook=_reject_duplicate_keys)
Comment on lines +255 to +258
create drift. The APM-owned sidecar is compared as JSON, ignoring object-key
order and formatting while retaining every field and list order. Changed
commands, ownership markers, or execution order still report `modified`, as
do malformed JSON and duplicate keys. `unrecorded` findings fail

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

apm audit --ci: false 'modified' drift on .claude/apm-hooks.json when the consumer's settings.json already has hook events (key order)

2 participants