Index: src/services/guardianship/handshake.js
===================================================================
--- src/services/guardianship/handshake.js	(revision 66d97b80b4d102bdf33f881136323c103204358b)
+++ src/services/guardianship/handshake.js	(revision 026173008d0d963628475bb6c9caa53d040cea7c)
@@ -341,4 +341,18 @@
     notify(site.slug, { kind: 'offer_rejected', offer: offerId });
     return { status: 202, id: offerId, url: offerId };
+  }
+
+  // §1 is flat in BOTH directions. The Offer path above bars a ward from
+  // offering to guard; this is the mirror: an actor that already guards wards
+  // must not become a ward itself. Only the ward's own accept can create that
+  // state, so the candidate and the existing guardians pass through untouched.
+  //
+  // Without it the inconsistency would also be invisible. actorProps() picks
+  // one role with an if/else and would publish shaer:guardians while dropping
+  // shaer:isGuardian, so this account keeps routing its wards' escalations
+  // locally while every remote §4 check reads it as malformed and drops it —
+  // a ward believing it is watched over when it is not, silent on both sides.
+  if (me === offer.ward_uri && relations.listWards(site.slug).length) {
+    return { status: 403, error: 'a_guardian_cannot_be_guarded' };
   }
 
Index: test/guardianship.test.js
===================================================================
--- test/guardianship.test.js	(revision 66d97b80b4d102bdf33f881136323c103204358b)
+++ test/guardianship.test.js	(revision 026173008d0d963628475bb6c9caa53d040cea7c)
@@ -119,4 +119,31 @@
   assert.equal(r.status, 403);
   assert.equal(r.error, 'a_ward_cannot_guard');
+});
+
+test('a guardian cannot be guarded either: §1 is flat in both directions', async () => {
+  // parent guards kid by now, and gran (a guardian, so free to offer) offers
+  // to guard parent. The offer itself is fine; only parent's accept would
+  // make one actor ward and guardian at once.
+  assert.ok(G.listWards('parent').length, 'parent already guards someone');
+  const off = await G.handleGuardianshipOutbox(gran, {
+    type: 'Offer', object: { type: 'Relationship', subject: ME, relationship: 'shaer:Guardian', object: GRAN },
+  });
+  assert.equal(off.status, 202, 'a guardian may still offer to guard');
+  const id = offerIdFrom(off);
+
+  const r = await G.handleGuardianshipOutbox(parent, { type: 'Accept', object: id });
+  assert.equal(r.status, 403);
+  assert.equal(r.error, 'a_guardian_cannot_be_guarded');
+
+  // Nothing recorded, and the actor document still reads as a pure guardian.
+  // Without the check actorProps() would have flipped it to a ward silently.
+  assert.deepEqual(G.listGuardians('parent'), []);
+  const doc = AP.buildActor('https://test.example', parent);
+  assert.equal(doc['shaer:isGuardian'], true);
+  assert.equal(doc['shaer:guardians'], undefined);
+
+  // The candidate and the existing guardians are untouched by the check, so
+  // the offer is refusable in the ordinary way rather than stuck.
+  await G.handleGuardianshipOutbox(parent, { type: 'Reject', object: id });
 });
 
