JS push() in setMacroJsOverride code corrupts macro icon and title

I’ve written a custom macro with a hidden parameter field that maintains a history of the macro’s parameters. I accomplish this using AJS.MacroBrowser.setMacroJsOverride with the “manipulateMarkup” hook. In the hook code I amend the hidden history parameter with the new settings. Code snippet (from mymacro.js):

AJS.MacroBrowser.setMacroJsOverride('my-macro', {
	fields: {
        string: {
            "history": function (params, options) {
				return AJS.MacroBrowser.ParameterFields["_hidden"](params, options);
            }
        }
    },

	// This gets called AFTER the user clicks "Save", but BEFORE new values are retrieved from the form.
	manipulateMarkup: 
		function (metadata) {
			// Original values (from "selectedMacro" settings)
			let oparms, oimpact, oprobability;
			let today = new Date().toISOString().slice(0, 10);
			
			// New values (from the form)
			let impact = AJS.$('#macro-param-impact')[0].value;			
			let probability = AJS.$('#macro-param-probability')[0].value;
			let hist = AJS.$('#macro-param-history');
			
			let record = {"date": today, "impact": impact, "probability": probability};
			
			// Check if we're editing an existing macro, or adding a new one
			if (typeof AJS.MacroBrowser.settings.selectedMacro != 'undefined') {
				// Editing an existing macro. Check to see if probability or impact changed.
				oparms = AJS.MacroBrowser.settings.selectedMacro.params;
				oimpact = oparms["impact"];
				oprobability = oparms["probability"];
				
				if ((oimpact != impact) || (oprobability != probability)) {
					try {
						let jobj = JSON.parse(hist[0].value);
						jobj.push(record);    // <--- PROBLEM: Corrupts macro icon and title text in page edit
						hist.val( JSON.stringify(jobj) );
					} catch (e) {
						console.error(e.name);
					}
				}
			} else {
				// Creating a new one
				let jobj = JSON.parse("[]");
				jobj.push(record);    // <--- PROBLEM: Corrupts macro icon and title text in page edit
				hist.val( JSON.stringify(jobj) );
			}
	}
});

The pertinent section of atlassian-plugin.xml:

	<xhtml-macro key="my-macro" name="my-macro"
		class= ...
		icon="/download/resources/${atlassian.plugin.key}/images/SysEngGroup_Logo.png">
		<category name="content" />
		<parameters>
			<parameter name="identifier" type="string" required="true" >
            	<option key="showValueInPlaceholder" value="true" />
           	</parameter>
			<parameter name="type" type="enum" required="true" > ... </parameter>
			<parameter name="probability" type="enum" required="true" > ... </parameter>
			<parameter name="impact" type="enum" required="true" > ... </parameter>
			<parameter name="status" type="enum" required="true" > ... </parameter>
			<parameter name="history" type="string" />
		</parameters>
	</xhtml-macro>

Everything works, but there’s a side effect that I’m having trouble hunting down. In the previous code, I’ve labeled two lines with a “PROBLEM” comment. For some reason, the JS array push operation leaves the macro icon and title (in the page edit dialog) corrupted. The icon reverts to the default page-looking icon, and the title contains the contents of the history parameter. The corrupted macro looks like:
image ← Corrupted macro icon & title

If I comment out the push() operations, the icon and title are correct (but the history doesn’t get amended, of course). The macro then looks like:
image ← Correct looking macro

I’ve stepped in with the debugger and confirmed that the macro metadata is still intact. (I.e. the icon setting still points to the correct file, and the title says what I expect it to). I’m sure there’s a step I’m missing, or a flag I haven’t set (“I changed something in the form…”), but documentation being what it is…

Any thoughts from the community?