Summary
hooks/rules/error_kb.py ErrorFixNudgeRule.evaluate() does tool_args = data.get('toolArgs', {}) or {} then tool_args.get('command', ''). When the runtime passes toolArgs as a non-empty string (e.g. a JSON string), or {} keeps the string and .get() raises 'str' object has no attribute 'get'.
Impact (Facts)
~/.copilot/markers/audit.jsonl recorded 367 entries: event=postToolUse rule=error-fix-nudge decision=error detail="'str' object has no attribute 'get'".
- Hooks fail-open, so work is not blocked, but the rule never runs (no error→fix→learn nudge) and the audit log is polluted.
Root cause
toolArgs shape is not normalized; a string value is not coerced/parsed.
Fix
Normalize toolArgs: if it is a JSON string, parse it to a dict; otherwise coerce any non-dict to {}. This also preserves the learn.py marker-clear behavior when args arrive as a JSON string.
Acceptance criteria
Summary
hooks/rules/error_kb.pyErrorFixNudgeRule.evaluate()doestool_args = data.get('toolArgs', {}) or {}thentool_args.get('command', ''). When the runtime passestoolArgsas a non-empty string (e.g. a JSON string),or {}keeps the string and.get()raises'str' object has no attribute 'get'.Impact (Facts)
~/.copilot/markers/audit.jsonlrecorded 367 entries:event=postToolUse rule=error-fix-nudge decision=error detail="'str' object has no attribute 'get'".Root cause
toolArgsshape is not normalized; a string value is not coerced/parsed.Fix
Normalize
toolArgs: if it is a JSON string, parse it to a dict; otherwise coerce any non-dict to{}. This also preserves the learn.py marker-clear behavior when args arrive as a JSON string.Acceptance criteria
toolArgsno longer crashes the rule.toolArgsis parsed so the nudge still clears on learn.py.tests/test_hook_rules_more.pypasses.