Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions .agents/scripts/supervisor-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1947,13 +1947,25 @@ find_task_issue_number() {

#######################################
# Get the identity string for task claiming (t165)
# Uses AIDEVOPS_IDENTITY env var, falls back to user@hostname
# Priority: AIDEVOPS_IDENTITY env > GitHub username (cached) > user@hostname
# The GitHub username is preferred because TODO.md assignees typically use
# GitHub usernames (e.g., assignee:marcusquinn), not user@host format.
#######################################
get_aidevops_identity() {
if [[ -n "${AIDEVOPS_IDENTITY:-}" ]]; then
echo "$AIDEVOPS_IDENTITY"
return 0
fi

# Try GitHub username (cached for the session to avoid repeated API calls)
if [[ -z "${_CACHED_GH_USERNAME:-}" ]]; then
_CACHED_GH_USERNAME=$(gh api user --jq '.login' 2>/dev/null || echo "")
fi
if [[ -n "$_CACHED_GH_USERNAME" ]]; then
echo "$_CACHED_GH_USERNAME"
return 0
fi

local user host
user=$(whoami 2>/dev/null || echo "unknown")
host=$(hostname -s 2>/dev/null || echo "local")
Expand Down Expand Up @@ -2020,8 +2032,12 @@ cmd_claim() {
current_assignee=$(get_task_assignee "$task_id" "$todo_file")

if [[ -n "$current_assignee" ]]; then
if [[ "$current_assignee" == "$identity" ]]; then
log_info "$task_id already claimed by you (assignee:$identity)"
# Use check_task_claimed for consistent fuzzy matching (handles
# username vs user@host mismatches)
local claimed_other=""
claimed_other=$(check_task_claimed "$task_id" "$project_root" 2>/dev/null) || true
if [[ -z "$claimed_other" ]]; then
log_info "$task_id already claimed by you (assignee:$current_assignee)"
return 0
fi
log_error "$task_id is claimed by assignee:$current_assignee"
Expand Down Expand Up @@ -2115,7 +2131,10 @@ cmd_unclaim() {
return 0
fi

if [[ "$current_assignee" != "$identity" ]]; then
# Use check_task_claimed for consistent fuzzy matching
local claimed_other=""
claimed_other=$(check_task_claimed "$task_id" "$project_root" 2>/dev/null) || true
if [[ -n "$claimed_other" ]]; then
log_error "$task_id is claimed by assignee:$current_assignee, not by you (assignee:$identity)"
return 1
fi
Expand Down Expand Up @@ -2171,11 +2190,25 @@ check_task_claimed() {
local identity
identity=$(get_aidevops_identity)

# Claimed by self = OK
# Exact match = claimed by self
if [[ "$current_assignee" == "$identity" ]]; then
return 0
fi

# Fuzzy match: assignee might be just a username while identity is user@host,
# or vice versa. Also check the local username (whoami) and GitHub username.
local local_user
local_user=$(whoami 2>/dev/null || echo "")
local gh_user="${_CACHED_GH_USERNAME:-}"
local identity_user="${identity%%@*}" # Strip @host portion
Comment on lines +2202 to +2203

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fuzzy matching logic may not work as expected if the AIDEVOPS_IDENTITY environment variable is set. In that scenario, get_aidevops_identity() returns early, and the _CACHED_GH_USERNAME variable is never populated. Consequently, the check against gh_user inside check_task_claimed() will always fail unless the assignee is an empty string.

To ensure fuzzy matching against the GitHub username works reliably in all cases, the GitHub username should be fetched within this function if it hasn't been cached already.

Suggested change
local gh_user="${_CACHED_GH_USERNAME:-}"
local identity_user="${identity%%@*}" # Strip @host portion
# Ensure GitHub username is cached for fuzzy matching, even if AIDEVOPS_IDENTITY is set.
if [[ -z "${_CACHED_GH_USERNAME:-}" ]]; then
_CACHED_GH_USERNAME=$(gh api user --jq '.login' 2>/dev/null || echo "")
fi
local gh_user="${_CACHED_GH_USERNAME:-}"
local identity_user="${identity%%@*}" # Strip @host portion


if [[ "$current_assignee" == "$local_user" ]] ||
[[ "$current_assignee" == "$gh_user" ]] ||
[[ "$current_assignee" == "$identity_user" ]] ||
[[ "${current_assignee%%@*}" == "$identity_user" ]]; then
return 0
fi

# Claimed by someone else
echo "$current_assignee"
return 1
Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Tasks with no open blockers - ready to work on. Use `/ready` to refresh this lis
- Notes: PR #548 merged. Guard arithmetic with `|| true` to prevent silent exit under `set -e`.

- [ ] t169 Fix `aidevops update` skipping agent deployment — pass `--non-interactive` to setup.sh #bug #setup ~15m (ai:10m) ref:GH#550 assignee:marcusquinn@Marcus-MacBook-Pro started:2026-02-08T17:33:28Z logged:2026-02-08
- Notes: `cmd_update()` in aidevops.sh calls `bash setup.sh` without `--non-interactive`, so interactive prompts silently skip in non-TTY contexts and agents never deploy.
- Notes: `cmd_update()` in aidevops.sh calls `bash setup.sh` without `--non-interactive`, so interactive prompts silently skip in non-TTY contexts and agents never deploy. BLOCKED: Re-prompt dispatch failed: ambiguous_skipped_ai

- [ ] t170 Fix `import-credentials` ignoring multi-tenant credential files #bug #credentials ~30m (ai:20m) ref:GH#553 logged:2026-02-08
- Notes: `cmd_import_credentials()` in secret-helper.sh reads credentials.sh directly but with multi-tenant active, that file is a loader script. Actual credentials are in `tenants/{tenant}/credentials.sh`.
Expand Down
Loading