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.
-
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
-
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)".
-
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.
-
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.
-
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.
-
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.
-
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_null — is_human_manifest({"agent_id": "x", "admin": None}) is True.
test_is_human_manifest_missing_key — is_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_dict — is_human_manifest({"admin": {}}) and {"admin": {"karma": True}} are False.
test_is_human_manifest_non_dict — is_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.
handshake.is_human: docstring/behavior mismatch on missing
adminkey, plus three divergent copies of the human/agent predicateSummary
handshake.is_human(src/lingtai_kernel/handshake.py:34-40) documents itself as "admin key explicitly set to null", but its implementationdata.get("admin") is Noneis alsoTruewhen the key is simply absent — so a malformed or hand-created.agent.jsonwith noadminfield classifies as human, which makesis_alivereturnTrueunconditionally and lets mail delivery and karma treat a dead directory as a live human. The same semantic predicate is independently re-implemented inmaintenance/retention.py:673-676(_is_human_manifest) and a third time inintrinsics/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
adminfield of.agent.json: real agents always carry anadmindict (the manifest schema atsrc/lingtai/init_schema.py:98types it asdict, and agent creation defaults it viakwargs.setdefault("admin", {"karma": True})atsrc/lingtai/agent.py:96), while human mailboxes carryadmin: null.The classifier in
src/lingtai_kernel/handshake.py:34-40reads:The docstring says "explicitly set to null", but
data.get("admin") is Nonecannot distinguish an explicit"admin": nullfrom a manifest that lacks the key entirely. So any.agent.jsonthat omitsadmin— 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:Humans do not write heartbeats, so they are always "alive". A missing-
admindirectory 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 insrc/lingtai_kernel/intrinsics/system/karma.py(which gates onis_alivein 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-19warns:and
tests/test_karma.py:92repeats 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: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) setsprotected_agent=bool(protected_reasons) and not is_humanandprotected_reasons=[] if is_human else protected_reasons— so a manifest missingadminhas 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: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.pythat states the missing-key semantics and why, with the other two sites delegating to it, and theis_humandocstring telling the truth.Why did the design get here?
is_humanpredates 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 refactoringis_humaninto 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) returnsTruefor a readable.agent.jsonwhoseadminkey isnullor absent. Its docstring claims explicit-null only.is_alive(path)(handshake.py:43-59, short-circuit at line 50) returnsTrueunconditionally for anythingis_humanclassifies 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-admindirectory 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_agentprotection 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."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.
Add a public dict-level helper in
src/lingtai_kernel/handshake.py, next tois_human:Rewrite
is_human(handshake.py:34-40) to delegate: keep the path-based signature, replace the body's final line withreturn is_human_manifest(data), and fix the docstring to say "admin key is null or missing" with a pointer tois_human_manifestfor the rationale. Also update theis_alivedocstring (line 47, "Human agents (admin=null)") to "(admin null or missing)".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:(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 Noneis equivalent tomanifest.get("admin") is None— so this is a pure de-duplication.Delegate the email-manager copy. In
src/lingtai_kernel/intrinsics/email/manager.py:169, replaceidentity.get("admin") is Nonewithis_human_manifest(identity)(import at module top from...handshake). The identity card is a dict with the sameadminconvention (see src/lingtai_kernel/services/mail.py:504, which copiesadmininto identity cards), so semantics are unchanged.Export it. Add
is_human_manifestwhereverhandshakesymbols are re-exported (checksrc/lingtai_kernel/__init__.py), so downstream packages (src/lingtai) can adopt it too.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.
Optional hardening (small, behavior-preserving): in
is_human, emit alogging.debugwhen 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
"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_aliveFalse → 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.lingtairoots and stamp"admin": nullinto 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."admin" not in manifestclause vs. handshake's "explicitly set to null" docstring).maintenance/retention.pyandintrinsics/email/manager.pygaining an import ofhandshakecould in principle create a cycle;handshake.pyimports onlyworkdir, stdlibjson/time/pathlib, so no cycle exists today. Keep the helper free of new imports to preserve that.is_human_manifestacceptingNone/non-dict input returnsFalse(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_null—is_human_manifest({"agent_id": "x", "admin": None})isTrue.test_is_human_manifest_missing_key—is_human_manifest({"agent_id": "x"})isTrue(locks in the documented fallback so any future tightening must consciously update this test).test_is_human_manifest_admin_dict—is_human_manifest({"admin": {}})and{"admin": {"karma": True}}areFalse.test_is_human_manifest_non_dict—is_human_manifest(None),is_human_manifest([])areFalse.test_is_human_missing_admin_key_path— write a manifest withoutadminto a tmp agent dir; assertis_human(path)isTrueandis_alive(path)isTruewith 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— assertretention._is_human_manifestandhandshake.is_human_manifestagree on the four manifest shapes above (guards against future re-divergence); plus an integration case: an agent dir whose manifest omitsadminreportsis_human: truein 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 withoutadminyieldsis_human: truein 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.