Index: test/guardianship.test.js
===================================================================
--- test/guardianship.test.js	(revision 5327324cd028236fac938e6870fe3f6c9366ab1f)
+++ test/guardianship.test.js	(revision 69bd7471bb6b8a573c6a17fe0d93eafa9cbf6cd7)
@@ -160,4 +160,31 @@
   const stillPending = G.offersCollection(`${SAM}/queues/offers`, 'sam', SAM).orderedItems.filter((o) => o.id === id);
   assert.deepEqual(stillPending, [], 'the handshake is void, not left hanging');
+});
+
+test('an Offer from a candidate that is already a ward is refused on arrival (§4.2)', async () => {
+  // The kind path. Nobody has accepted anything yet, so refusing here
+  // discloses nothing about anyone's position, and a candidate who is merely
+  // misconfigured gets told what is wrong while that is still all it means.
+  const viv = site('s13', 'viv');   // adopted first, then tries to guard
+  const zed = site('s14', 'zed');   // the would-be ward
+  const bo = site('s15', 'bo');     // adopts Viv
+  const [VIV, ZED, BO] = [A('viv'), A('zed'), A('bo')];
+
+  const adopt = await G.handleGuardianshipOutbox(bo, {
+    type: 'Offer', object: { type: 'Relationship', subject: VIV, relationship: 'shaer:Guardian', object: BO },
+  });
+  await G.handleGuardianshipOutbox(viv, { type: 'Accept', object: adopt.id });
+  assert.equal(G.listGuardians('viv').length, 1, 'Viv is a ward');
+
+  const handled = await G.handleGuardianshipInbox(zed, {
+    id: `${VIV}/offers/x1`, type: 'Offer', actor: VIV, to: [ZED],
+    object: { type: 'Relationship', subject: ZED, relationship: 'shaer:Guardian', object: VIV },
+  });
+  assert.equal(handled, true, 'the activity is handled — and handling it means refusing it');
+  assert.deepEqual(
+    G.offersCollection(`${ZED}/queues/offers`, 'zed', ZED).orderedItems, [],
+    'never stored, so it never sits in Zed\'s queue looking like a decision to make',
+  );
+  assert.deepEqual(G.listGuardians('zed'), []);
 });
 
