Skip to content

fix(integrations): dispatch bob commands via bob run - #4492

Open
temp-droid wants to merge 3 commits into
github:mainfrom
temp-droid:fix/4491-bob-cli-dispatch
Open

fix(integrations): dispatch bob commands via bob run#4492
temp-droid wants to merge 3 commits into
github:mainfrom
temp-droid:fix/4491-bob-cli-dispatch

Conversation

@temp-droid

Copy link
Copy Markdown

Description

Fixes #4491.

BobIntegration never overrode build_exec_args(), so it inherited the
IntegrationBase no-op returning None. Every workflow command:/prompt:
step targeting Bob failed with "CLI not found or not installed" even with bob
on PATH. Bob Shell has a headless entry point (bob run), so this dispatches
through it.

Also overrides build_command_invocation(), which was inherited from
IntegrationBase with a hardcoded . and rendered /speckit.specify where
skills-mode projects install .bob/skills/speckit-specify/. The class already
computes the right separator in effective_invoke_separator().

requires_cli stays False — dispatch is opt-in by overriding the method,
mirroring CursorAgentIntegration. Same fix shape as #2631.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

Added 6 tests to tests/integrations/test_integration_bob.py (62 pass in that
file); tests/test_agent_config_consistency.py passes.

Verified end-to-end against Bob Shell 2.0.1: a workspace skill at
.bob/skills/speckit-marker/ was reached through
dispatch_command → build_exec_args → bob run, returning status: success
with one UseSkillTool call.

Note: the full suite shows 11 failures in the bash/Python parity tests. They are
identical on main without this change — 11 failed / 7702 passed on main,
11 failed / 7708 passed here, same test set — and pass when run in isolation.

AI Disclosure

  • I did use AI assistance (describe below)

Investigation, patch and tests written with Claude; reviewed and verified by me.

`BobIntegration` never overrode `build_exec_args()`, so it inherited the
`IntegrationBase` no-op returning `None`. Callers read `None` as "this CLI is
unavailable", so every workflow command/prompt step targeting Bob reported
`CLI not found or not installed` even with `bob` on PATH.

`build_command_invocation()` was inherited too, rendering `/speckit.specify`
where skills-mode projects install `.bob/skills/speckit-specify/`.
@temp-droid
temp-droid requested a review from mnriem as a code owner September 9, 2026 15:24
@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Sep 9, 2026
@mnriem
mnriem requested a balanced review from Copilot September 9, 2026 17:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Legacy-mode and dotted extension commands currently produce incorrect invocations.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Enables Bob workflow dispatch through its headless bob run interface.

Changes:

  • Adds Bob CLI argument and command-invocation construction.
  • Adds regression tests for dispatch formats and IDE-first configuration.
File summaries
File Description
src/specify_cli/integrations/bob/__init__.py Implements Bob headless dispatch.
tests/integrations/test_integration_bob.py Tests Bob dispatch behavior.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/specify_cli/integrations/bob/__init__.py Outdated
Comment thread src/specify_cli/integrations/bob/__init__.py Outdated
@mnriem

mnriem commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks — the bob run dispatch approach is right, and the requires_cli=False reasoning is sound. The re-review found two invocation-rendering bugs to fix before merge: (1) effective_invoke_separator() is called without project_root, so it always falls back to Bob's fresh-project default (-) and gets legacy mode wrong — thread the project context through; (2) only the first separator is converted, so a dotted extension command like speckit.git.commit renders /speckit-git.commit in skills mode, but the installed skill is speckit-git-commit — convert all segments. Re-request once addressed.

@mnriem mnriem added the author-awaiting Waiting on author response label Sep 9, 2026
temp-droid and others added 2 commits September 10, 2026 19:18
…dispatched into

`build_command_invocation()` called `effective_invoke_separator()` with no
arguments, so `is_skills_mode()` never reached its disk-detection branch
(bob/__init__.py:223-228) and always returned the fresh-project default.
`dispatch_command()` does receive the workflow project root, but the shared
two-argument `build_command_invocation(command_name, args)` contract -- which
eight integrations implement -- had nowhere to put it.

Two consequences, measured against a real `.bob/commands/speckit.specify.md`
project:

  legacy project, speckit.specify     -> /speckit-specify        (want /speckit.specify)
  skills project, speckit.git.commit  -> /speckit-git.commit     (want /speckit-git-commit)

(a) Every existing Bob 1.x install is dispatched a skills-mode invocation
naming a command it does not have, so the run fails exactly as it did before
dispatch was implemented.

(b) Only the `speckit.` prefix was converted, leaving inner dots. The
installed skill directory is `speckit-git-commit` -- `SkillsIntegration`
derives it with `stem.replace(".", "-")` (base.py:1759) and renders the
invocation the same way (base.py:1649) -- so a dotted extension command names
a skill that does not exist. The two layouts differ in both the separator and
the dot handling: skills flatten every dot, legacy commands keep them.

`_build_dispatch_prompt()` carries the project root from `dispatch_command()`
into invocation building. The base implementation is the call it replaces, so
the other integrations are unaffected; Bob overrides it to resolve the layout
from disk, falling back to the working directory, which is where
`dispatch_command` runs `bob` when no root is given.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqDT4qTj3sFBeg9tMRiXqZ
Upstream github#4425 (per-step integration configuration) landed after this
branch was pushed and collided with it in two ways.

The textual conflict is one region of dispatch_command(): github#4425 inserted
a validate_runtime_config() call on the line above the one this branch
changed.  The two edits are independent, so both survive -- validation
first, then _build_dispatch_prompt() builds the prompt.

The second collision is semantic and git does not flag it.  github#4425 widened
build_exec_args() with integration_args/integration_options and updated
all fifteen integrations that override it.  Bob's override arrives with
this branch, so github#4425 had nothing to update, and merging alone would
raise TypeError on every Bob dispatch -- the path this branch exists to
make work.  tests/integrations/test_base.py enumerates the registry and
catches it:

  AssertionError: bob
  TypeError: BobIntegration.build_exec_args() got an unexpected keyword
  argument 'integration_args'

Bob therefore takes the same four lines as every other integration: the
two keyword-only parameters and a validate_runtime_config() call first,
copied from CursorAgentIntegration and DroidIntegration.  Bob accepts no
per-step runtime configuration, so the inherited default -- which rejects
non-empty values rather than ignoring them -- is the correct behaviour.

Verified: 2915 integration tests pass; the registry test is red without
the Bob change and green with it; ruff reports nothing on the three
changed files; and dispatch renders /speckit.specify for a legacy
project, /speckit-git-commit for a skills project.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqDT4qTj3sFBeg9tMRiXqZ

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The reviewed changes have regression coverage and no unresolved blocking issues.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Lite (auto)

Note

Copilot is running an experiment and ran this review at Lite.

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

Labels

author-awaiting Waiting on author response triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Bob integration cannot dispatch — build_exec_args() unimplemented

3 participants