fix(resolve): detect a type's agent process from its manifest, not a fixed list - #631
Open
kappa4 wants to merge 1 commit into
Open
fix(resolve): detect a type's agent process from its manifest, not a fixed list#631kappa4 wants to merge 1 commit into
kappa4 wants to merge 1 commit into
Conversation
…fixed list
_agmsg_agent_binaries mapped a type to the process names that identify it with
a hardcoded case whose last arm answered "claude codex gemini". That arm was
not the exception it reads as: every type added since the list was written and
never given an arm -- cursor, grok-build, hermes -- landed there, and their
type.conf detect_proc (cursor-agent, grok) was never consulted at all.
The consequence is a cross-type identity mix-up. agmsg_pid_is_agent accepted an
enclosing Claude Code process as a cursor agent, so agmsg_agent_pid walking up
from a slash command found the CALLER's claude process and called it the cursor
member's own. agmsg_resolve_project step 1 then read that session's project
marker, and a cursor member's project resolved to the project of whoever was
asking:
resolve("<member worktree>", cursor)
before -> .../leader-project (the caller's project)
after -> .../member-worktree (the member's own)
Read detect_proc from the type registry, dropping its glob tokens since the
matcher already tries "<bin>-*" for each entry, and keep the case as the
fallback for a manifest without the key (antigravity, copilot). Memoized per
type: this runs inside a ppid walk of up to 20 hops, and a manifest read is a
filesystem scan.
The same misresolution reaches actas lock ownership and instance-id keying for
those types, which is the wider reason to fix it at the mapping rather than at
each caller.
Closes fujibee#626.
fujibee
added a commit
that referenced
this pull request
Aug 7, 2026
…t/--type/--team doctor previously required <project> <type> up front, which was backwards: a reporter who does not already know which project/type to look at cannot use a doctor that demands one. Positional <project> <type> is dropped, not kept for compatibility -- doctor has few enough callers that carrying a stale positional form alongside flags that mean something different by default would be its own source of confusion. New interface: doctor default = whole install (every team, project, type) doctor --project <path> narrow to one project doctor --type <type> narrow to one type doctor --team <team> narrow to one team (all three combine; --redacted and --help unchanged) Argument parsing stays separate from scope-building, so a future flag change is a parsing-only change. Also fixes three defects found by running the rebuilt scope against the real installation: - A type whose delivery_modes is nothing but "off" (agmsg-app, hermes -- the desktop app owns its own send/receive UI) was queried against delivery.sh anyway, which exits 1 by design (nothing to report); doctor turned that into a warning, breaking the exit-code contract on an otherwise healthy install. Now checked via the manifest before querying, the same agmsg_type_get/delivery_modes key PR #631 already reads. - A (project, type) group with nothing held, no warning, and nothing beyond a bare idle mode line now collapses to one line instead of a 6-line block -- unreadable at real scale (dozens of such groups on a real install). - The "watch processes: N alive, M stale pidfiles" line default runtime status emits is an installation-wide fact (it scans the whole run/ directory), not a per-(project, type) fact; it and the warning it can produce now surface once, not once per group using default runtime status. Read-only behavior, the 0/1/2 exit-code contract, and --redacted's masking behavior are unchanged. SKILL.md and the per-type templates are not wired to this yet -- deliberately deferred to a separate PR once this scope shape is settled, so the 9 templates + the Windows dispatcher aren't rewritten twice. Addresses part of #267.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #626.
_agmsg_agent_binaries(scripts/lib/resolve-project.sh) maps an agent type to the process names that identify it using a hardcodedcase. Its last arm answersclaude codex gemini, and that arm is not the exception it reads as — every type added since the list was written and never given an arm lands there:type.conf detect_proc_agmsg_agent_binaries(before)cursor-agent cursor-agent-*claude codex geminigrok grok-*claude codex geminiclaude codex geminiThe manifests'
detect_procwas never consulted at all, which also made this a live counterexample to the "types are discovered from manifests instead of hardcoded whitelists" contract intype-registry.sh's header.Consequence
agmsg_pid_is_agent <pid> cursoraccepted a Claude Code process, becauseclaudeis in the fallback list.agmsg_agent_pid, walking up from a slash command, therefore found the caller's claude process and called it the cursor member's own agent process;agmsg_resolve_projectstep 1 then read that session's project marker. A cursor member's project resolved to the project of whoever was asking:Steps 2 and 3 would both have got it right — the ancestor walk returns the member's own registered path — but step 1 preempts them.
The same misresolution reaches actas lock ownership and instance-id keying (#93) for these types, which is why this is fixed at the mapping rather than at each call site.
What changes
_agmsg_agent_binariesreadsdetect_procfrom the type registry, dropping its glob tokens since the matcher already tries"<bin>-*"for each entry. Thecaseis kept as the fallback for a manifest with no such key (antigravity, copilot), so those types are unaffected.The lookup is memoized per type: it runs inside
agmsg_agent_pid's ppid walk of up to 20 hops, and a manifest read is a filesystem scan per hop.Tests
Three regression tests in
tests/test_resolve_project.bats:agent-binaries: process names come from the type manifest, not a hardcoded list—cursor→cursor-agent,grok-build→grok,codexunchangedpid-is-agent: a claude process is not accepted as an agent of another type— and is still accepted as its ownresolve: a member's project is not rewritten to the caller's by a cross-type marker— the end-to-end shapeThe last two are skipped on Windows (they fake
argv[0]viaexec -a, per #349). The manifest-less fallback arm (antigravity, copilot) is preserved but not pinned by a new test.tests/test_resolve_project.batspasses on this branch; the full suite is left to CI.Relationship to #625
Filed alongside the
despawn/resetdefect cluster in #625, whose third cause is where I hit this. This change stands on its own — it touches no file that cluster does, and needs nothing from it.