Index: test/object-links.test.js
===================================================================
--- test/object-links.test.js	(revision eb36688598f8d808a62a4fd9336d9bbdf02cbf9f)
+++ test/object-links.test.js	(revision cc3cf7dfa8380e09d7fbe9a8e4875036d28eaede)
@@ -6,6 +6,6 @@
 const dbMod = await import('../src/config/database.js');
 dbMod.initializeDatabase();
-const { extractObjectLinkTags, timelineObjectLinks } = await import('../src/services/ActivityPubService.js');
-const AP = { extractObjectLinkTags, timelineObjectLinks };
+const { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson } = await import('../src/services/ActivityPubService.js');
+const AP = { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson };
 
 test('extractObjectLinkTags keeps AS2-profiled ld+json and activity+json Links; drops plain links and mentions', () => {
@@ -31,2 +31,30 @@
   assert.equal(AP.timelineObjectLinks('not json'), undefined);
 });
+
+// FEP-044f: object-level quote properties are the common representation.
+test('extractQuoteUrl reads quote / quoteUrl / quoteUri / _misskey_quote (string or embedded object)', () => {
+  assert.equal(AP.extractQuoteUrl({ quote: 'https://s/objects/9' }), 'https://s/objects/9');
+  assert.equal(AP.extractQuoteUrl({ quoteUrl: 'https://s/q1' }), 'https://s/q1');
+  assert.equal(AP.extractQuoteUrl({ quoteUri: 'https://s/q2' }), 'https://s/q2');
+  assert.equal(AP.extractQuoteUrl({ _misskey_quote: 'https://s/q3' }), 'https://s/q3');
+  assert.equal(AP.extractQuoteUrl({ quote: { type: 'Link', href: 'https://s/q4' } }), 'https://s/q4');
+  assert.equal(AP.extractQuoteUrl({ content: 'no quote' }), null);
+});
+
+test('extractLinkJson normalises an object-level quote into one FEP-e232 Link (rel _misskey_quote)', () => {
+  const json = AP.extractLinkJson({ content: 'nice', quoteUrl: 'https://s/objects/9' });
+  const arr = AP.timelineObjectLinks(json);
+  assert.equal(arr.length, 1);
+  assert.equal(arr[0].href, 'https://s/objects/9');
+  assert.ok(arr[0].rel.some((r) => r.includes('quote')));
+});
+
+test('extractLinkJson merges a real FEP-e232 Link with an object-level quote, deduped by href', () => {
+  const note = {
+    tag: [{ type: 'Link', mediaType: 'application/activity+json', href: 'https://s/objects/9' }],
+    quoteUrl: 'https://s/objects/9',   // same target → not duplicated
+  };
+  const arr = AP.timelineObjectLinks(AP.extractLinkJson(note));
+  assert.equal(arr.length, 1);
+  assert.equal(arr[0].href, 'https://s/objects/9');
+});
