diff --git a/app/assets/javascripts/content_editor/extensions/bullet_list.js b/app/assets/javascripts/content_editor/extensions/bullet_list.js
index dfd9cac4c6611063d8e5cdc10973bd37f5ac4f99..148d0df49fc6a6dd62b2e9ad56785d485fb9ec62 100644
--- a/app/assets/javascripts/content_editor/extensions/bullet_list.js
+++ b/app/assets/javascripts/content_editor/extensions/bullet_list.js
@@ -18,7 +18,7 @@ export default BulletList.extend({
       bullet: {
         default: '*',
         parseHTML(element) {
-          const bullet = getMarkdownSource(element)?.charAt(0);
+          const bullet = getMarkdownSource(element)?.trim().charAt(0);
 
           return '*+-'.includes(bullet) ? bullet : '*';
         },
diff --git a/app/assets/javascripts/content_editor/extensions/ordered_list.js b/app/assets/javascripts/content_editor/extensions/ordered_list.js
index d0b760010de1696a8b01bee80bd1ae410b613acd..221ce0af86b1a73bf53c1bf1fd7b725a8b53e3a5 100644
--- a/app/assets/javascripts/content_editor/extensions/ordered_list.js
+++ b/app/assets/javascripts/content_editor/extensions/ordered_list.js
@@ -17,7 +17,7 @@ export default OrderedList.extend({
 
       parens: {
         default: false,
-        parseHTML: (element) => /^[0-9]+\)/.test(getMarkdownSource(element)),
+        parseHTML: (element) => /^[0-9]+\)/.test(getMarkdownSource(element)?.trim()),
       },
     };
   },
diff --git a/app/assets/javascripts/content_editor/extensions/sourcemap.js b/app/assets/javascripts/content_editor/extensions/sourcemap.js
index f02d0c2ca52a89d438c05f57772222949eccef28..d982f54785e0784528702467f22d4998bb598bb7 100644
--- a/app/assets/javascripts/content_editor/extensions/sourcemap.js
+++ b/app/assets/javascripts/content_editor/extensions/sourcemap.js
@@ -1,4 +1,5 @@
 import { Extension } from '@tiptap/core';
+import { getMarkdownSource, docHasSourceMap } from '../services/markdown_sourcemap';
 import Audio from './audio';
 import Blockquote from './blockquote';
 import Bold from './bold';
@@ -34,6 +35,8 @@ export default Extension.create({
   name: 'sourcemap',
 
   addGlobalAttributes() {
+    const preserveMarkdown = () => gon.features?.preserveMarkdown;
+
     return [
       {
         types: [
@@ -77,10 +80,18 @@ export default Extension.create({
            */
           sourceMarkdown: {
             default: null,
+            parseHTML: (element) => preserveMarkdown() && getMarkdownSource(element),
             renderHTML: () => '',
           },
           sourceMapKey: {
             default: null,
+            parseHTML: (element) => preserveMarkdown() && element.dataset.sourcepos,
+            renderHTML: () => '',
+          },
+          sourceTagName: {
+            default: null,
+            parseHTML: (element) =>
+              preserveMarkdown() && docHasSourceMap(element) && element.tagName.toLowerCase(),
             renderHTML: () => '',
           },
         },
diff --git a/app/assets/javascripts/content_editor/extensions/task_list.js b/app/assets/javascripts/content_editor/extensions/task_list.js
index a6394ac23c8663aec5d89841f81aeec9c3240dec..864ece017bd99df756251d4fb98243650b4758c6 100644
--- a/app/assets/javascripts/content_editor/extensions/task_list.js
+++ b/app/assets/javascripts/content_editor/extensions/task_list.js
@@ -30,7 +30,7 @@ export default TaskList.extend({
       bullet: {
         default: '*',
         parseHTML(element) {
-          const bullet = getMarkdownSource(element)?.charAt(0);
+          const bullet = getMarkdownSource(element)?.trim().charAt(0);
           return '*+-'.includes(bullet) ? bullet : '*';
         },
       },
diff --git a/app/assets/javascripts/content_editor/services/markdown_sourcemap.js b/app/assets/javascripts/content_editor/services/markdown_sourcemap.js
index de230c370b1fd104f095cbb45f7ab13182652779..206a9a27a218e0c7030ef32dcbfdc0eff96ac090 100644
--- a/app/assets/javascripts/content_editor/services/markdown_sourcemap.js
+++ b/app/assets/javascripts/content_editor/services/markdown_sourcemap.js
@@ -1,5 +1,10 @@
 import { isString } from 'lodash';
 
+export const docHasSourceMap = (element) => {
+  const commentNode = element.ownerDocument.body.lastChild;
+  return Boolean(commentNode?.nodeName === '#comment' && isString(commentNode.textContent));
+};
+
 export const getFullSource = (element) => {
   const commentNode = element.ownerDocument.body.lastChild;
 
@@ -32,14 +37,26 @@ export const getMarkdownSource = (element) => {
     if (!source.length) return undefined;
 
     for (let i = range.start.row; i <= range.end.row; i += 1) {
-      if (i === range.start.row) {
-        elSource += source[i].substring(range.start.col);
+      if (i === range.start.row && i === range.end.row) {
+        // include leading whitespace in the sourcemap
+        if (!source[i]?.substring(0, range.start.col).trim()) {
+          range.start.col = 0;
+        }
+        elSource += source[i].substring(range.start.col, range.end.col + 1);
+      } else if (i === range.start.row) {
+        // include leading whitespace in the sourcemap
+        if (!source[i]?.substring(0, range.start.col).trim()) {
+          range.start.col = 0;
+        }
+        elSource += source[i]?.substring(range.start.col) || '';
+      } else if (i === range.end.row) {
+        elSource += `\n${source[i]?.substring(0, range.end.col + 1) || ''}`;
       } else {
         elSource += `\n${source[i]}` || '';
       }
     }
 
-    return elSource.trim();
+    return elSource;
   } catch {
     return undefined;
   }
diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js
index e81b0c3f0e0917cb3b9801825e4d4925db7e1dcb..c2ec6b911752db8f88dd13008f2c3dcf2bcc3161 100644
--- a/app/assets/javascripts/content_editor/services/serialization_helpers.js
+++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js
@@ -5,7 +5,7 @@ const defaultAttrs = {
   th: { colspan: 1, rowspan: 1, colwidth: null, align: 'left' },
 };
 
-const defaultIgnoreAttrs = ['sourceMarkdown', 'sourceMapKey'];
+const defaultIgnoreAttrs = ['sourceMarkdown', 'sourceMapKey', 'sourceTagName'];
 
 const ignoreAttrs = {
   dd: ['isTerm'],
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index 30c2e516b337b0a33bc5c0ed5f2eb620255db23b..59502ade012cd0db21207c5f615c70b484fc278a 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -32,6 +32,10 @@ module WikiActions
     before_action :page, only: [:show, :edit, :update, :history, :destroy, :diff]
     before_action :load_sidebar, except: [:pages]
 
+    before_action do
+      push_frontend_feature_flag(:preserve_markdown, container)
+    end
+
     before_action only: [:show, :edit, :update] do
       @valid_encoding = valid_encoding?
     end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 275bdca6e0518c91a0889d1fce87ce1cdc0606e6..6667945f7b54ee5673b0e71eeafca7be3cede9dc 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -44,6 +44,7 @@ class Projects::IssuesController < Projects::ApplicationController
   before_action :authorize_read_code!, only: [:related_branches]
 
   before_action do
+    push_frontend_feature_flag(:preserve_markdown, project)
     push_frontend_feature_flag(:issues_grid_view)
     push_frontend_feature_flag(:service_desk_ticket)
     push_frontend_feature_flag(:issues_list_drawer, project)
diff --git a/config/feature_flags/development/preserve_markdown.yml b/config/feature_flags/development/preserve_markdown.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9483654f8d503e16c49617ed1b89fd7632c7b181
--- /dev/null
+++ b/config/feature_flags/development/preserve_markdown.yml
@@ -0,0 +1,8 @@
+---
+name: preserve_markdown
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/160709
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/474407
+milestone: '17.3'
+type: development
+group: group::knowledge
+default_enabled: false
diff --git a/ee/app/controllers/groups/epics_controller.rb b/ee/app/controllers/groups/epics_controller.rb
index 53bef6b1d4427db6d9a0d3a5c53b2d6c716d5a4e..a66d87e03e6c6d8b29cc1872c25c634405bf94bd 100644
--- a/ee/app/controllers/groups/epics_controller.rb
+++ b/ee/app/controllers/groups/epics_controller.rb
@@ -18,6 +18,7 @@ class Groups::EpicsController < Groups::ApplicationController
   after_action :log_epic_show, only: :show
 
   before_action do
+    push_frontend_feature_flag(:preserve_markdown, @group)
     push_frontend_feature_flag(:notifications_todos_buttons, current_user)
     push_frontend_feature_flag(:namespace_level_work_items, @group)
     push_force_frontend_feature_flag(:namespace_level_work_items, epic_work_items_enabled?)
diff --git a/spec/frontend/content_editor/services/markdown_sourcemap_spec.js b/spec/frontend/content_editor/services/markdown_sourcemap_spec.js
index f1eb361782f549335271b049e490eb19f77dcd5b..96663881eccabd4398d8494bc83d1f4835ed97e2 100644
--- a/spec/frontend/content_editor/services/markdown_sourcemap_spec.js
+++ b/spec/frontend/content_editor/services/markdown_sourcemap_spec.js
@@ -1,87 +1,55 @@
 import { builders } from 'prosemirror-test-builder';
-import { Extension } from '@tiptap/core';
+import Bold from '~/content_editor/extensions/bold';
 import BulletList from '~/content_editor/extensions/bullet_list';
+import Code from '~/content_editor/extensions/code';
+import Italic from '~/content_editor/extensions/italic';
 import ListItem from '~/content_editor/extensions/list_item';
 import TaskList from '~/content_editor/extensions/task_list';
 import TaskItem from '~/content_editor/extensions/task_item';
-import Paragraph from '~/content_editor/extensions/paragraph';
+import Sourcemap from '~/content_editor/extensions/sourcemap';
+import Strike from '~/content_editor/extensions/strike';
 import markdownDeserializer from '~/content_editor/services/gl_api_markdown_deserializer';
-import { getMarkdownSource, getFullSource } from '~/content_editor/services/markdown_sourcemap';
+import { getFullSource } from '~/content_editor/services/markdown_sourcemap';
 import { createTestEditor } from '../test_utils';
-
-const BULLET_LIST_MARKDOWN = `+ list item 1
-+ list item 2
-  - embedded list item 3`;
-const BULLET_LIST_HTML = `<ul data-sourcepos="1:1-3:24" dir="auto">
-  <li data-sourcepos="1:1-1:13">list item 1</li>
-  <li data-sourcepos="2:1-3:24">list item 2
-    <ul data-sourcepos="3:3-3:24">
-      <li data-sourcepos="3:3-3:24">embedded list item 3</li>
-    </ul>
-  </li>
-</ul>`;
-
-const MALFORMED_BULLET_LIST_HTML =
-  `<ul data-sourcepos="1:1-3:24" dir="auto">
-  <li data-sourcepos="1:1-1:13">list item 1</li>` +
-  // below line has malformed sourcepos
-  `<li data-sourcepos="5:1-5:24">list item 2
-    <ul data-sourcepos="3:3-3:24">
-      <li data-sourcepos="3:3-3:24">embedded list item 3</li>
-    </ul>
-  </li>
-</ul>`;
-
-const BULLET_TASK_LIST_MARKDOWN = `- [ ] list item 1
-+ [x] checked list item 2
-  + [ ] embedded list item 1
-  - [x] checked embedded list item 2`;
-const BULLET_TASK_LIST_HTML = `<ul data-sourcepos="1:1-4:36" class="task-list" dir="auto">
-  <li data-sourcepos="1:1-1:17" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox"> list item 1</li>
-  <li data-sourcepos="2:1-4:36" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" checked> checked list item 2
-    <ul data-sourcepos="3:3-4:36" class="task-list">
-      <li data-sourcepos="3:3-3:28" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox"> embedded list item 1</li>
-      <li data-sourcepos="4:3-4:36" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" checked> checked embedded list item 2</li>
-    </ul>
-  </li>
-</ul>`;
-
-const SourcemapExtension = Extension.create({
-  // lets add `source` attribute to every element using `getMarkdownSource`
-  addGlobalAttributes() {
-    return [
-      {
-        types: [Paragraph.name, BulletList.name, ListItem.name],
-        attributes: {
-          source: {
-            parseHTML: (element) => {
-              const source = getMarkdownSource(element);
-              return source;
-            },
-          },
-        },
-      },
-    ];
-  },
-});
+import {
+  BULLET_LIST_MARKDOWN,
+  BULLET_LIST_HTML,
+  MALFORMED_BULLET_LIST_HTML,
+  BULLET_TASK_LIST_MARKDOWN,
+  BULLET_TASK_LIST_HTML,
+  PARAGRAPHS_MARKDOWN,
+  PARAGRAPHS_HTML,
+} from '../test_constants';
 
 const tiptapEditor = createTestEditor({
-  extensions: [BulletList, ListItem, TaskList, TaskItem, SourcemapExtension],
+  extensions: [BulletList, ListItem, TaskList, TaskItem, Sourcemap, Bold, Italic, Strike, Code],
 });
 
-const { doc, bulletList, listItem, taskList, taskItem, paragraph } = builders(tiptapEditor.schema);
+const { doc, bulletList, listItem, taskList, taskItem, paragraph, bold, italic, strike, code } =
+  builders(tiptapEditor.schema);
+
+const text = (val) => tiptapEditor.state.schema.text(val);
+
+const sourceAttrs = jest.fn().mockImplementation((sourceTagName, sourceMapKey, sourceMarkdown) => ({
+  sourceTagName,
+  sourceMapKey,
+  sourceMarkdown,
+}));
 
 const bulletListDoc = () =>
   doc(
     bulletList(
-      { bullet: '+', source: '+ list item 1\n+ list item 2\n  - embedded list item 3' },
-      listItem({ source: '+ list item 1' }, paragraph('list item 1')),
+      { bullet: '+', ...sourceAttrs('ul', '1:1-3:24', BULLET_LIST_MARKDOWN) },
+      listItem(sourceAttrs('li', '1:1-1:13', '+ list item 1'), paragraph('list item 1')),
       listItem(
-        { source: '+ list item 2\n  - embedded list item 3' },
+        sourceAttrs('li', '2:1-3:24', '+ list item 2\n  - embedded list item 3'),
         paragraph('list item 2'),
         bulletList(
-          { bullet: '-', source: '- embedded list item 3' },
-          listItem({ source: '- embedded list item 3' }, paragraph('embedded list item 3')),
+          { bullet: '-', ...sourceAttrs('ul', '3:3-3:24', '  - embedded list item 3') },
+          listItem(
+            sourceAttrs('li', '3:3-3:24', '  - embedded list item 3'),
+            paragraph('embedded list item 3'),
+          ),
         ),
       ),
     ),
@@ -90,13 +58,18 @@ const bulletListDoc = () =>
 const bulletListDocWithMalformedSourcepos = () =>
   doc(
     bulletList(
-      { bullet: '+', source: '+ list item 1\n+ list item 2\n  - embedded list item 3' },
-      listItem({ source: '+ list item 1' }, paragraph('list item 1')),
+      { bullet: '+', ...sourceAttrs('ul', '1:1-3:24', BULLET_LIST_MARKDOWN) },
+      listItem(sourceAttrs('li', '1:1-1:13', '+ list item 1'), paragraph('list item 1')),
       listItem(
+        // source not included for out of bounds list item here
+        sourceAttrs('li', '5:1-5:24'),
         paragraph('list item 2'),
         bulletList(
-          { bullet: '-', source: '- embedded list item 3' },
-          listItem({ source: '- embedded list item 3' }, paragraph('embedded list item 3')),
+          { bullet: '-', ...sourceAttrs('ul', '3:3-3:24', '  - embedded list item 3') },
+          listItem(
+            sourceAttrs('li', '3:3-3:24', '  - embedded list item 3'),
+            paragraph('embedded list item 3'),
+          ),
         ),
       ),
     ),
@@ -105,27 +78,36 @@ const bulletListDocWithMalformedSourcepos = () =>
 const bulletTaskListDoc = () =>
   doc(
     taskList(
-      {
-        bullet: '-',
-        source:
-          '- [ ] list item 1\n+ [x] checked list item 2\n  + [ ] embedded list item 1\n  - [x] checked embedded list item 2',
-      },
-      taskItem({ source: '- [ ] list item 1' }, paragraph('list item 1')),
+      { bullet: '-', ...sourceAttrs('ul', '1:1-4:36', BULLET_TASK_LIST_MARKDOWN) },
+      taskItem(sourceAttrs('li', '1:1-1:17', '- [ ] list item 1'), paragraph('list item 1')),
       taskItem(
         {
-          source:
+          ...sourceAttrs(
+            'li',
+            '2:1-4:36',
             '+ [x] checked list item 2\n  + [ ] embedded list item 1\n  - [x] checked embedded list item 2',
+          ),
           checked: true,
         },
         paragraph('checked list item 2'),
         taskList(
           {
             bullet: '+',
-            source: '+ [ ] embedded list item 1\n  - [x] checked embedded list item 2',
+            ...sourceAttrs(
+              'ul',
+              '3:3-4:36',
+              '  + [ ] embedded list item 1\n  - [x] checked embedded list item 2',
+            ),
           },
-          taskItem({ source: '+ [ ] embedded list item 1' }, paragraph('embedded list item 1')),
           taskItem(
-            { source: '- [x] checked embedded list item 2', checked: true },
+            sourceAttrs('li', '3:3-3:28', '  + [ ] embedded list item 1'),
+            paragraph('embedded list item 1'),
+          ),
+          taskItem(
+            {
+              ...sourceAttrs('li', '4:3-4:36', '  - [x] checked embedded list item 2'),
+              checked: true,
+            },
             paragraph('checked embedded list item 2'),
           ),
         ),
@@ -133,6 +115,62 @@ const bulletTaskListDoc = () =>
     ),
   );
 
+const paragraphsDoc = () =>
+  doc(
+    paragraph(
+      sourceAttrs(
+        'p',
+        '1:1-1:233',
+        'You could bold with **asterisks** or you could bold with __underscores__. You could even bold with <strong>strong</strong> or <b>b</b> html tags. You could add newlines in your paragraph, or `code` tags with `` nested `backticks` ``.',
+      ),
+      text('You could bold with '),
+      bold(sourceAttrs('strong', '1:21-1:33', '**asterisks**'), 'asterisks'),
+      text(' or you could bold with '),
+      bold(sourceAttrs('strong', '1:58-1:72', '__underscores__'), 'underscores'),
+      text('. You could even bold with '),
+      bold(sourceAttrs('strong'), 'strong'),
+      text(' or '),
+      bold(sourceAttrs('b'), 'b'),
+      text(' html tags. You could add newlines in your paragraph, or '),
+      code(sourceAttrs('code', '1:193-1:196', 'code'), 'code'),
+      text(' tags with '),
+      code(sourceAttrs('code', '1:211-1:230', ' nested `backticks` '), 'nested `backticks`'),
+      text('.'),
+    ),
+    paragraph(
+      sourceAttrs(
+        'p',
+        '3:1-3:144',
+        'You could italicise with *asterisks* or you could italicise with _underscores_. You could even italicise with <em>em</em> or <i>i</i> html tags.',
+      ),
+      text('You could italicise with '),
+      italic(sourceAttrs('em', '3:26-3:36', '*asterisks*'), 'asterisks'),
+      text(' or you could italicise with '),
+      italic(sourceAttrs('em', '3:66-3:78', '_underscores_'), 'underscores'),
+      text('. You could even italicise with '),
+      italic(sourceAttrs('em'), 'em'),
+      text(' or '),
+      italic(sourceAttrs('i'), 'i'),
+      text(' html tags.'),
+    ),
+    paragraph(
+      sourceAttrs(
+        'p',
+        '5:1-5:154',
+        "As long as you don't touch a paragraph, it will ~~discard~~ <s>destroy</s> <del>delete</del> <strike>remove</strike> preserve the original markdown style.",
+      ),
+      text("As long as you don't touch a paragraph, it will "),
+      strike(sourceAttrs('del', '5:49-5:59', '~~discard~~'), 'discard'),
+      text(' '),
+      strike({ ...sourceAttrs('s'), htmlTag: 's' }, 'destroy'),
+      text(' '),
+      strike(sourceAttrs('del'), 'delete'),
+      text(' '),
+      strike({ ...sourceAttrs('strike'), htmlTag: 'strike' }, 'remove'),
+      text(' preserve the original markdown style.'),
+    ),
+  );
+
 describe('content_editor/services/markdown_sourcemap', () => {
   describe('getFullSource', () => {
     it.each`
@@ -154,24 +192,63 @@ describe('content_editor/services/markdown_sourcemap', () => {
     });
   });
 
-  it.each`
-    description                               | sourceMarkdown               | sourceHTML                    | expectedDoc
-    ${'bullet list'}                          | ${BULLET_LIST_MARKDOWN}      | ${BULLET_LIST_HTML}           | ${bulletListDoc}
-    ${'bullet list with malformed sourcepos'} | ${BULLET_LIST_MARKDOWN}      | ${MALFORMED_BULLET_LIST_HTML} | ${bulletListDocWithMalformedSourcepos}
-    ${'bullet task list'}                     | ${BULLET_TASK_LIST_MARKDOWN} | ${BULLET_TASK_LIST_HTML}      | ${bulletTaskListDoc}
-  `(
-    'gets markdown source for a rendered $description',
-    async ({ sourceMarkdown, sourceHTML, expectedDoc }) => {
-      const { document } = await markdownDeserializer({
-        render: () => ({
-          body: sourceHTML,
-        }),
-      }).deserialize({
-        schema: tiptapEditor.schema,
-        markdown: sourceMarkdown,
-      });
-
-      expect(document.toJSON()).toEqual(expectedDoc().toJSON());
-    },
-  );
+  describe('when preserveMarkdown feature is enabled', () => {
+    beforeEach(() => {
+      gon.features = { preserveMarkdown: true };
+    });
+
+    afterEach(() => {
+      gon.features = {};
+    });
+
+    it.each`
+      description                               | sourceMarkdown               | sourceHTML                    | expectedDoc
+      ${'bullet list'}                          | ${BULLET_LIST_MARKDOWN}      | ${BULLET_LIST_HTML}           | ${bulletListDoc}
+      ${'bullet list with malformed sourcepos'} | ${BULLET_LIST_MARKDOWN}      | ${MALFORMED_BULLET_LIST_HTML} | ${bulletListDocWithMalformedSourcepos}
+      ${'bullet task list'}                     | ${BULLET_TASK_LIST_MARKDOWN} | ${BULLET_TASK_LIST_HTML}      | ${bulletTaskListDoc}
+      ${'paragraphs with inline elements'}      | ${PARAGRAPHS_MARKDOWN}       | ${PARAGRAPHS_HTML}            | ${paragraphsDoc}
+    `(
+      'gets markdown source for a rendered $description',
+      async ({ sourceMarkdown, sourceHTML, expectedDoc }) => {
+        const { document } = await markdownDeserializer({
+          render: () => ({
+            body: sourceHTML,
+          }),
+        }).deserialize({
+          schema: tiptapEditor.schema,
+          markdown: sourceMarkdown,
+        });
+
+        expect(document.content.toJSON()).toEqual(expectedDoc().content.toJSON());
+      },
+    );
+  });
+
+  describe('when preserveMarkdown feature is disabled', () => {
+    beforeEach(() => {
+      sourceAttrs.mockImplementation(() => ({}));
+    });
+
+    it.each`
+      description                               | sourceMarkdown               | sourceHTML                    | expectedDoc
+      ${'bullet list'}                          | ${BULLET_LIST_MARKDOWN}      | ${BULLET_LIST_HTML}           | ${bulletListDoc}
+      ${'bullet list with malformed sourcepos'} | ${BULLET_LIST_MARKDOWN}      | ${MALFORMED_BULLET_LIST_HTML} | ${bulletListDocWithMalformedSourcepos}
+      ${'bullet task list'}                     | ${BULLET_TASK_LIST_MARKDOWN} | ${BULLET_TASK_LIST_HTML}      | ${bulletTaskListDoc}
+      ${'paragraphs with inline elements'}      | ${PARAGRAPHS_MARKDOWN}       | ${PARAGRAPHS_HTML}            | ${paragraphsDoc}
+    `(
+      'does not include any source information for $description',
+      async ({ sourceMarkdown, sourceHTML, expectedDoc }) => {
+        const { document } = await markdownDeserializer({
+          render: () => ({
+            body: sourceHTML,
+          }),
+        }).deserialize({
+          schema: tiptapEditor.schema,
+          markdown: sourceMarkdown,
+        });
+
+        expect(document.content.toJSON()).toEqual(expectedDoc().content.toJSON());
+      },
+    );
+  });
 });
diff --git a/spec/frontend/content_editor/test_constants.js b/spec/frontend/content_editor/test_constants.js
index b56a6272de665716e3a3858ad2ed090e08c9b911..fbbe3a3feae40cd8e9a4a7bbaea8df7c28ee7bd1 100644
--- a/spec/frontend/content_editor/test_constants.js
+++ b/spec/frontend/content_editor/test_constants.js
@@ -59,3 +59,66 @@ export const RESOLVED_USER_HTML =
 
 export const RESOLVED_VULNERABILITY_HTML =
   '<p data-sourcepos="1:1-1:56" dir="auto"><a href="/gitlab-org/gitlab-shell/-/security/vulnerabilities/1" data-reference-type="vulnerability" data-original="[vulnerability:1]" data-link="false" data-link-reference="false" data-project="2" data-vulnerability="1" data-container="body" data-placement="top" title="oh no!" class="gfm gfm-vulnerability has-tooltip">[vulnerability:1]</a> <a href="/gitlab-org/gitlab-shell/-/security/vulnerabilities/1" data-reference-type="vulnerability" data-original="[vulnerability:1]" data-link="false" data-link-reference="false" data-project="2" data-vulnerability="1" data-container="body" data-placement="top" title="oh no!" class="gfm gfm-vulnerability has-tooltip">[vulnerability:1]</a>+ <a href="/gitlab-org/gitlab-shell/-/security/vulnerabilities/1" data-reference-type="vulnerability" data-original="[vulnerability:1]" data-link="false" data-link-reference="false" data-project="2" data-vulnerability="1" data-container="body" data-placement="top" title="oh no!" class="gfm gfm-vulnerability has-tooltip">[vulnerability:1]</a>+s</p>';
+
+export const BULLET_LIST_MARKDOWN = `+ list item 1
++ list item 2
+  - embedded list item 3`;
+
+export const BULLET_LIST_HTML = `<ul data-sourcepos="1:1-3:24" dir="auto">
+  <li data-sourcepos="1:1-1:13">list item 1</li>
+  <li data-sourcepos="2:1-3:24">list item 2
+    <ul data-sourcepos="3:3-3:24">
+      <li data-sourcepos="3:3-3:24">embedded list item 3</li>
+    </ul>
+  </li>
+</ul>`;
+
+export const MALFORMED_BULLET_LIST_HTML =
+  `<ul data-sourcepos="1:1-3:24" dir="auto">
+  <li data-sourcepos="1:1-1:13">list item 1</li>` +
+  // below line has malformed sourcepos
+  `<li data-sourcepos="5:1-5:24">list item 2
+    <ul data-sourcepos="3:3-3:24">
+      <li data-sourcepos="3:3-3:24">embedded list item 3</li>
+    </ul>
+  </li>
+</ul>`;
+
+export const BULLET_TASK_LIST_MARKDOWN = `- [ ] list item 1
++ [x] checked list item 2
+  + [ ] embedded list item 1
+  - [x] checked embedded list item 2`;
+
+export const BULLET_TASK_LIST_HTML = `<ul data-sourcepos="1:1-4:36" class="task-list" dir="auto">
+  <li data-sourcepos="1:1-1:17" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox"> list item 1</li>
+  <li data-sourcepos="2:1-4:36" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" checked> checked list item 2
+    <ul data-sourcepos="3:3-4:36" class="task-list">
+      <li data-sourcepos="3:3-3:28" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox"> embedded list item 1</li>
+      <li data-sourcepos="4:3-4:36" class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" checked> checked embedded list item 2</li>
+    </ul>
+  </li>
+</ul>`;
+
+export const PARAGRAPHS_MARKDOWN = `You could bold with **asterisks** or you could bold with __underscores__. You could even bold with <strong>strong</strong> or <b>b</b> html tags. You could add newlines in your paragraph, or \`code\` tags with \`\` nested \`backticks\` \`\`.
+
+You could italicise with *asterisks* or you could italicise with _underscores_. You could even italicise with <em>em</em> or <i>i</i> html tags.
+
+As long as you don't touch a paragraph, it will ~~discard~~ <s>destroy</s> <del>delete</del> <strike>remove</strike> preserve the original markdown style.`;
+
+export const PARAGRAPHS_HTML = `<p data-sourcepos="1:1-1:233" dir="auto">
+  You could bold with <strong data-sourcepos="1:21-1:33">asterisks</strong> or you could bold with
+  <strong data-sourcepos="1:58-1:72">underscores</strong>. You could even bold with
+  <strong>strong</strong> or <b>b</b> html tags. You could add newlines in your paragraph,
+  or <code data-sourcepos="1:193-1:196">code</code> tags with
+  <code data-sourcepos="1:211-1:230">nested \`backticks\`</code>.
+</p>
+<p data-sourcepos="3:1-3:144" dir="auto">
+  You could italicise with <em data-sourcepos="3:26-3:36">asterisks</em> or
+  you could italicise with <em data-sourcepos="3:66-3:78">underscores</em>.
+  You could even italicise with <em>em</em> or <i>i</i> html tags.
+</p>
+<p data-sourcepos="5:1-5:154" dir="auto">
+  As long as you don't touch a paragraph, it will
+  <del data-sourcepos="5:49-5:59">discard</del> <s>destroy</s> <del>delete</del>
+  <strike>remove</strike> preserve the original markdown style.
+</p>`;