Skip to content

fix(bus): validate --assignee against the enabled-agents roster - #151

Open
asachs01 wants to merge 2 commits into
mainfrom
fix/validate-assignee-roster
Open

fix(bus): validate --assignee against the enabled-agents roster#151
asachs01 wants to merge 2 commits into
mainfrom
fix/validate-assignee-roster

Conversation

@asachs01

@asachs01 asachs01 commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • Neither create-task nor update-task validated that --assignee named a real agent, so a typo silently routed a task into the void (task_1786739337901_81484880, boss follow-up 2026-08-14).
  • Validation is checked at the CLI boundary (src/cli/bus.ts), not inside createTask()/updateTask() themselves — those library functions are also called directly by the unit test suite with lightweight fixture agent names that have no real roster behind them. Validating at the library layer broke 52 of 70 tests before being rescoped here, to where the actual failure (a human typing --assignee) lives.
  • human/user bypass the check — deliberate sentinels for [HUMAN] tasks, never real agent names.
  • Checks name existence only, not enabled state — a disabled agent is still a legitimate reassignment target.

Test plan

  • tsc --noEmit clean
  • tests/unit/bus/ + tests/unit/cli/ — 507/507 passing
  • Live smoke test in an isolated worktree build: rejects a nonexistent assignee (no task created), accepts a real roster agent, human sentinel still works
  • Confirmed a rejected update-task --assignee leaves the existing task's real state unmutated

Rebase note (2026-09-04, dev)

Branch conflicted against main after #168 (check-batch-staleness) landed — resolvePaths() gained a 4th ctxRootOverride param, colliding with the two validateAssigneeArg call sites here. Rebased by hand rather than taking a naive auto-resolution on either side, because a naive resolution would have silently reintroduced the exact bug #168's own comment warns about ("omitting it silently discards a CTX_ROOT override, every bus path falls back to real homedir") — kept main's 4-arg resolvePaths() call at both sites and appended validateAssigneeArg immediately after it.

Verifying the rebase surfaced a real regression beyond the conflict itself: validateAssigneeArg exited 1 on any org whose agent-directory scan came back empty — which broke tests/integration/bus-task-error-handling-cli.test.ts (drives the real compiled CLI against a synthetic testorg with no roster fixture behind it). This PR's own original verification only covered tests/unit/bus/ + tests/unit/cli/, never tests/integration/, so it shipped unnoticed. Fixed in a separate, independently-reviewable commit (30071f94): fail open when the roster comes back completely empty for the org, since a real org always has agents on disk and an empty result means there was nothing to check against, not that every name is invalid. Full suite now passes (2647-2649/2654 across runs; the only variance is a pre-existing, already-documented flaky real-chokidar timing test unrelated to this change), tsc clean. Independently re-reviewed by murph (diffed the resolution against the diagnosis, checked listAgents's source before trusting the fail-open behavior, confirmed the integration test genuinely exercises the scenario) — LGTM.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

…eate-task/update-task

Neither create-task nor update-task validated that --assignee named a real
agent, so a typo silently routed a task into the void (task_1786739337901_81484880,
boss follow-up 2026-08-14).

Checked at the CLI boundary (src/cli/bus.ts) rather than inside
createTask()/updateTask() themselves: those library functions are also
called directly by the unit test suite with lightweight fixture agent names
that have no real enabled-agents.json/orgs-directory roster behind them --
validating at the library layer broke 52 of 70 tests in task.test.ts before
this was rescoped to the CLI layer, where the actual failure mode (a human
typing --assignee) lives. 'human'/'user' bypass the check entirely -- they
are deliberate sentinels for [HUMAN] tasks, never real agent names (see the
assigned_to checks already in src/bus/task.ts). Checks name existence only,
not `enabled` state, since a disabled agent is still a legitimate
reassignment target.

Verified live (not just tsc+tests): rejects a nonexistent assignee with no
task created / no mutation applied to an existing task, accepts a real
roster agent, and the human/user sentinel still works, on a real build in
an isolated worktree.

tsc clean, 507 tests pass (tests/unit/bus/ + tests/unit/cli/).
Rebasing PR #151 onto main surfaced a real regression: the CLI-level
--assignee roster check exits 1 for any org whose agent directory scan
comes back empty, which is exactly what happens when
tests/integration/bus-task-error-handling-cli.test.ts drives the real
compiled CLI against a synthetic "testorg" with no orgs/testorg/agents/
fixture behind it. The original PR's own verification (507 tests,
tests/unit/bus/ + tests/unit/cli/ only) never ran that integration
suite, so this shipped unnoticed.

A real org always has agents on disk; an empty roster means the check
had nothing to validate against, not that every name is invalid.
Fail open in that case rather than block a legitimate reassignment.

Verified: tests/integration/bus-task-error-handling-cli.test.ts green
(10/10, was 1 failing), full suite 2647-2649/2654 pass depending on run
-- the only variance is dashboard/src/lib/__tests__/watcher-ingests-real-events.test.ts,
a pre-existing real-chokidar timing test that fails inconsistently under
full-suite load and passes clean in isolation (4/4), unrelated to this
change (already documented as an environment-flaky class in this repo's
CLAUDE.md). tsc clean.
@asachs01
asachs01 force-pushed the fix/validate-assignee-roster branch from 0a7d31b to 30071f9 Compare September 4, 2026 12:45
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 23 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: c9bf6d7e-c534-4d2d-a1fa-09704c45ede1

📥 Commits

Reviewing files that changed from the base of the PR and between e44bf20 and 30071f9.

📒 Files selected for processing (1)
  • src/cli/bus.ts

Comment @coderabbitai help to get the list of available commands.

@asachs01 asachs01 left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reviewed on behalf of murph (formal approval blocked -- shared identity can't approve its own PR).

Verified independently, not taken on report alone:

  1. Merge conflict resolution matches the diagnosis exactly -- resolvePaths(env.agentName, env.instanceId, env.org, env.ctxRoot) (main's 4-arg signature) at both call sites, validateAssigneeArg appended right after, both import lines kept. Confirmed via the actual diff.
  2. The new fail-open fix (30071f9): checked listAgents's actual source rather than trusting the description -- it's already designed to fail soft everywhere else (corrupt enabled-agents.json falls through, missing directories are skipped via existsSync guards, no throws), so roster.length === 0 -> return is consistent with the function's existing philosophy, not a new risk class. Residual case worth naming for the record: a genuinely misconfigured CTX_FRAMEWORK_ROOT in production would silently disable validation -- but that's an existing property of listAgents itself (pre-dates this PR) and would represent an abnormal daemon state independent of this change, not something this PR introduces.
  3. Confirmed tests/integration/bus-task-error-handling-cli.test.ts is real and does exercise update-task --assignee against ORG = "testorg" with no roster fixture -- the regression claim checks out, not just asserted.

LGTM. CI still finishing (Dashboard Build / Unit Tests in progress as of this review) -- once green, needs Aaron's actual approving click same as the other cortextos PRs (this repo requires 1 distinct-identity approval).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant