diff --git a/packages/docx-compare/src/tagged/terminalMarkCleanupParity.test.ts b/packages/docx-compare/src/tagged/terminalMarkCleanupParity.test.ts
new file mode 100644
index 00000000..4c871f73
--- /dev/null
+++ b/packages/docx-compare/src/tagged/terminalMarkCleanupParity.test.ts
@@ -0,0 +1,78 @@
+import { describe, expect } from 'vitest';
+import { XMLSerializer } from '@xmldom/xmldom';
+import { testAllure } from '../testing/allure-test.js';
+import { parseXml, serializeXml, acceptChanges, rejectChanges } from '@usejunior/docx-core';
+import { acceptAllChanges, rejectAllChanges } from './trackChangesAcceptorAst.js';
+const test = testAllure.epic('Document Comparison').withLabels({ feature: 'Terminal mark cleanup parity' });
+const W = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
+const subtree = (element: Element) => new XMLSerializer().serializeToString(element);
+const wrap = (body: string) => `${body}`;
+const paragraph = (kind: string, color: boolean, text: boolean) => '' +
+ `${color ? '' : ''}` +
+ `${text ? 'Surviving text' : ''}`;
+const cell = (p: string) => '' + p + '';
+
+describe('resolved terminal paragraph-mark properties', () => {
+ for (const [name, kind, nativeProject, astProject] of [
+ ['Accept', 'del', acceptChanges, acceptAllChanges], ['Reject', 'ins', rejectChanges, rejectAllChanges],
+ ['Accept inserted', 'ins', acceptChanges, acceptAllChanges], ['Reject deleted', 'del', rejectChanges, rejectAllChanges],
+ ] as const) {
+ const revisionTest = test.conformance({ spec: 'ECMA-376', edition: 5, part: 1, section: kind === 'del' ? '17.13.5.15' : '17.13.5.20' });
+ for (const inCell of [false, true]) {
+ for (const color of [false, true]) {
+ revisionTest(`${name} terminal ${inCell ? 'cell' : 'body'} mark ${color ? 'retains color' : 'drops empty rPr'}`, () => {
+ const p = paragraph(kind, color, true);
+ const input = wrap(inCell ? cell(p) : p);
+ const doc = parseXml(input);
+ nativeProject(doc);
+ expect(serializeXml(doc)).toBe(serializeXml(parseXml(astProject(input))));
+ expect(doc.getElementsByTagNameNS(W, 'p').length).toBe(1);
+ expect(doc.getElementsByTagNameNS(W, 't')[0]!.textContent).toBe('Surviving text');
+ expect(doc.getElementsByTagNameNS(W, 'rPr').length).toBe(color ? 1 : 0);
+ });
+ }
+ }
+
+ revisionTest(`${name} preserves the required empty last table-cell paragraph`, () => {
+ const input = wrap(cell(paragraph(kind, false, false)));
+ const doc = parseXml(input);
+ nativeProject(doc);
+ expect(doc.getElementsByTagNameNS(W, 'p').length).toBe(1);
+ expect(doc.getElementsByTagNameNS(W, 'rPr').length).toBe(0);
+ expect(serializeXml(doc)).toBe(serializeXml(parseXml(astProject(input))));
+ });
+
+ revisionTest(`${name} resolves one author while preserving a foreign terminal mark and formatting`, () => {
+ const foreign = paragraph(kind, true, true).replace('w:author="AI"', 'w:author="Human"');
+ const input = wrap(cell(paragraph(kind, false, true)) + cell(foreign));
+ const doc = parseXml(input);
+ const foreignBefore = subtree(doc.getElementsByTagNameNS(W, 'p')[1]!);
+ nativeProject(doc, { filter: e => e.getAttributeNS(W, 'author') === 'AI' });
+ expect(subtree(doc.getElementsByTagNameNS(W, 'p')[1]!)).toBe(foreignBefore);
+ expect(doc.getElementsByTagNameNS(W, 'p')[0]!.getElementsByTagNameNS(W, 'rPr').length).toBe(0);
+ expect(doc.getElementsByTagNameNS(W, 'p').length).toBe(2);
+ });
+ }
+
+ for (const [name, kind, project, ast] of [['Accept', 'del', acceptChanges, acceptAllChanges], ['Reject', 'ins', rejectChanges, rejectAllChanges]] as const) {
+ for (const metadata of ['', '']) {
+ for (const scope of ['pPr', 'rPr']) {
+ test.conformance({ spec: 'ECMA-376', edition: 5, part: 1, section: kind === 'del' ? '17.13.5.15' : '17.13.5.20' })(`${name} retains ${metadata.startsWith('