Skip to content

handshake.is_human docstring says "admin key explicitly set to null" but a missing key also classifies as human; predicate is triplicated across handshake, retention, and email manager #762

Description

@huangzesen

handshake.is_human: docstring/behavior mismatch on missing admin key, plus three divergent copies of the human/agent predicate

Summary

handshake.is_human (src/lingtai_kernel/handshake.py:34-40) documents itself as "admin key explicitly set to null", but its implementation data.get("admin") is None is also True when the key is simply absent — so a malformed or hand-created .agent.json with no admin field classifies as human, which makes is_alive return True unconditionally and lets mail delivery and karma treat a dead directory as a live human. The same semantic predicate is independently re-implemented in maintenance/retention.py:673-676 (_is_human_manifest) and a third time in intrinsics/email/manager.py:169. The docstring should be corrected and the three copies consolidated into one documented helper so the human/agent boundary cannot drift.

Narrative

The kernel distinguishes two kinds of "agent directories": real agents (which run a heartbeat loop) and human pseudo-agents (mailbox directories that a person reads — no process, no heartbeat). The signal for "this is a human" is the admin field of .agent.json: real agents always carry an admin dict (the manifest schema at src/lingtai/init_schema.py:98 types it as dict, and agent creation defaults it via kwargs.setdefault("admin", {"karma": True}) at src/lingtai/agent.py:96), while human mailboxes carry admin: null.

The classifier in src/lingtai_kernel/handshake.py:34-40 reads:

def is_human(path: str | Path) -> bool:
    """Check if the agent at *path* is a human (admin key explicitly set to null)."""
    try:
        data = manifest(path)
    except (FileNotFoundError, json.JSONDecodeError):
        return False
    return data.get("admin") is None

The docstring says "explicitly set to null", but data.get("admin") is None cannot distinguish an explicit "admin": null from a manifest that lacks the key entirely. So any .agent.json that omits admin — a hand-created mailbox, a truncated or partially-migrated manifest, a third-party tool writing a minimal {"agent_id": ..., "agent_name": ...} — silently reads as human.

That misclassification is not cosmetic, because is_alive (handshake.py:43-59) short-circuits on it:

if is_human(path):
    return True

Humans do not write heartbeats, so they are always "alive". A missing-admin directory therefore also becomes permanently alive: FilesystemMailService.send (src/lingtai_kernel/services/mail.py:161, if not is_alive(recipient_dir):) will happily deliver mail into a directory whose process died months ago, and every karma/nirvana action in src/lingtai_kernel/intrinsics/system/karma.py (which gates on is_alive in at least eight places) sees a phantom live peer.

The codebase already knows this is a trap — it just documents it in a test fixture instead of the function. tests/test_handshake.py:16-19 warns:

The admin field MUST be non-null — is_alive() short-circuits True for human pseudo-agents (admin=null), so a manifest without an explicit admin block makes every is_alive call return True regardless of heartbeat state.

and tests/test_karma.py:92 repeats the same caveat. When the invariant lives in test comments rather than in the classifier's own contract, every new call site re-learns it the hard way.

Worse, the predicate has since been re-implemented twice, and one copy encodes the opposite reading of intent. src/lingtai_kernel/maintenance/retention.py:673-676:

def _is_human_manifest(manifest: dict[str, Any] | None) -> bool:
    if not isinstance(manifest, dict):
        return False
    return "admin" not in manifest or manifest.get("admin") is None

This version spells out "admin" not in manifest or ... — i.e. the retention author deliberately treated missing as human, contradicting the handshake docstring while matching its behavior. In retention this matters concretely: _scan_agent (retention.py:274, 284-285) sets protected_agent=bool(protected_reasons) and not is_human and protected_reasons=[] if is_human else protected_reasons — so a manifest missing admin has its liveness/status protections stripped and its mail folders scanned for cleanup as if it were a human mailbox.

The third copy is src/lingtai_kernel/intrinsics/email/manager.py:169, operating on an identity-card dict rather than a manifest path:

summary["is_human"] = identity.get("admin") is None

Three independent implementations of one boundary predicate is exactly how the human/agent line drifts: a future fix to one copy (say, tightening handshake to explicit-null) would silently change mail-delivery and karma semantics while retention and email identity keep the old reading. Better looks like: one public, documented helper in handshake.py that states the missing-key semantics and why, with the other two sites delegating to it, and the is_human docstring telling the truth.

Why did the design get here? is_human predates retention and the email identity card; each later feature needed "is this manifest a human?" on data it already had in hand (a parsed dict, not a path), so each re-derived the predicate locally instead of refactoring is_human into a dict-level helper. The docstring was presumably written aspirationally ("explicitly set to null" is the documented convention for human mailboxes) without noticing .get() collapses the missing case into it.

Current behavior

  • is_human(path) (src/lingtai_kernel/handshake.py:34-40) returns True for a readable .agent.json whose admin key is null or absent. Its docstring claims explicit-null only.
  • is_alive(path) (handshake.py:43-59, short-circuit at line 50) returns True unconditionally for anything is_human classifies as human, regardless of heartbeat state.
  • FilesystemMailService.send (src/lingtai_kernel/services/mail.py:161) and all karma/nirvana liveness gates (src/lingtai_kernel/intrinsics/system/karma.py:96-204) therefore treat a missing-admin directory as a permanently live human.
  • _is_human_manifest (src/lingtai_kernel/maintenance/retention.py:673-676) re-implements the predicate with explicitly-permissive missing-key handling, feeding _scan_agent protection logic (retention.py:274, 284-285).
  • EmailManager._inject_identity (src/lingtai_kernel/intrinsics/email/manager.py:169) re-implements it a third time against identity-card dicts.
  • No production code in this repo ever writes "admin": null (grep finds only the handshake docstrings mentioning it), so existing human mailboxes may in fact rely on the missing-key fallback — meaning a silent tightening could break real deployments.

Detailed implementation plan

The primary deliverable is documentation-and-consolidation: make the permissive semantics explicit, official, and single-sourced. Tightening to explicit-null is discussed under Risks as a follow-up decision.

  1. Add a public dict-level helper in src/lingtai_kernel/handshake.py, next to is_human:

    def is_human_manifest(data: dict | None) -> bool:
        """Classify a parsed .agent.json dict as a human pseudo-agent.
    
        A manifest is human when its ``admin`` key is null — including
        when the key is absent entirely.  The missing-key fallback is
        intentional: kernel-created agents always carry an ``admin``
        dict (init_schema types it as dict; agent creation defaults it),
        so an absent key indicates a hand-created human mailbox.
        Consequence: is_alive() treats such directories as always alive
        (humans write no heartbeat), so mail delivery and karma will
        target them unconditionally.  Do not re-implement this predicate;
        import it.
        """
        if not isinstance(data, dict):
            return False
        return data.get("admin") is None
  2. Rewrite is_human (handshake.py:34-40) to delegate: keep the path-based signature, replace the body's final line with return is_human_manifest(data), and fix the docstring to say "admin key is null or missing" with a pointer to is_human_manifest for the rationale. Also update the is_alive docstring (line 47, "Human agents (admin=null)") to "(admin null or missing)".

  3. Delegate the retention copy. In src/lingtai_kernel/maintenance/retention.py, replace the body of _is_human_manifest (lines 673-676) with a call to the shared helper:

    from ..handshake import is_human_manifest as _is_human_manifest

    (or keep the private function as a one-line wrapper if the module prefers explicit defs). Behavior is identical — "admin" not in manifest or manifest.get("admin") is None is equivalent to manifest.get("admin") is None — so this is a pure de-duplication.

  4. Delegate the email-manager copy. In src/lingtai_kernel/intrinsics/email/manager.py:169, replace identity.get("admin") is None with is_human_manifest(identity) (import at module top from ...handshake). The identity card is a dict with the same admin convention (see src/lingtai_kernel/services/mail.py:504, which copies admin into identity cards), so semantics are unchanged.

  5. Export it. Add is_human_manifest wherever handshake symbols are re-exported (check src/lingtai_kernel/__init__.py), so downstream packages (src/lingtai) can adopt it too.

  6. Promote the invariant out of test comments. The fixture docstring in tests/test_handshake.py:16-19 and the comments in tests/test_karma.py:92,121,155 should reference the new helper's docstring as the canonical statement rather than restating the mechanism.

  7. Optional hardening (small, behavior-preserving): in is_human, emit a logging.debug when classification relied on the missing-key branch ("admin" not in data), so operators debugging phantom-alive directories get a breadcrumb without changing any outcome.

No schema or data migration is needed for this plan; existing manifests classify identically before and after.

Risks and alternatives

  • Alternative A — tighten to explicit null ("admin" in data and data["admin"] is None), as the stricter reading of the current docstring. This makes malformed manifests fail closed (not human → no heartbeat → is_alive False → mail bounces, karma refuses). It is arguably the safer long-term semantics, but it is a behavior change with real breakage risk: no code in this repo writes "admin": null, so human mailboxes in the wild may have been hand-created without the key and would abruptly stop receiving mail, and retention would begin treating them as unprotected non-human agents (retention.py:284-285 flips). If maintainers want this, it should be a separate issue with a migration step (scan .lingtai roots and stamp "admin": null into key-less human manifests) and a deprecation window. The consolidation in this issue is a prerequisite either way — tightening three divergent copies independently is how you get a half-tightened boundary.
  • Alternative B — document each copy in place, no consolidation. Minimal diff, but leaves three implementations that can drift; the retention copy already demonstrates drift in stated intent (explicit "admin" not in manifest clause vs. handshake's "explicitly set to null" docstring).
  • Risk of the chosen plan: import direction. maintenance/retention.py and intrinsics/email/manager.py gaining an import of handshake could in principle create a cycle; handshake.py imports only workdir, stdlib json/time/pathlib, so no cycle exists today. Keep the helper free of new imports to preserve that.
  • Risk: is_human_manifest accepting None/non-dict input returns False (matching retention's guard); callers passing a raw parsed JSON scalar get non-human, which is the current retention behavior — no change.

Testing plan

Add to tests/test_handshake.py:

  • test_is_human_manifest_explicit_nullis_human_manifest({"agent_id": "x", "admin": None}) is True.
  • test_is_human_manifest_missing_keyis_human_manifest({"agent_id": "x"}) is True (locks in the documented fallback so any future tightening must consciously update this test).
  • test_is_human_manifest_admin_dictis_human_manifest({"admin": {}}) and {"admin": {"karma": True}} are False.
  • test_is_human_manifest_non_dictis_human_manifest(None), is_human_manifest([]) are False.
  • test_is_human_missing_admin_key_path — write a manifest without admin to a tmp agent dir; assert is_human(path) is True and is_alive(path) is True with no heartbeat file (documents the phantom-alive consequence end-to-end).

Add to tests/test_retention_report.py:

  • test_is_human_manifest_delegates_to_handshake — assert retention._is_human_manifest and handshake.is_human_manifest agree on the four manifest shapes above (guards against future re-divergence); plus an integration case: an agent dir whose manifest omits admin reports is_human: true in the retention report (extends the existing assertion at tests/test_retention_report.py:197).

Add to tests/test_email_identity.py:

  • test_identity_missing_admin_key_is_human — an identity card without admin yields is_human: true in check/read summaries, mirroring the existing explicit-null case at tests/test_email_identity.py:139-153.

Generated by a Claude Fable 5 deep review of the lingtai-kernel codebase.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationfable-5Filed by Claude Fable 5 repo review

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions