fix(integrations): dispatch Forge via prompt flag only - #4668
Open
hmohammadi wants to merge 1 commit into
Open
hmohammadi wants to merge 1 commit into
hmohammadi wants to merge 1 commit into
Conversation
Fixes github#4666. `ForgeIntegration` never overrode `build_exec_args()`, so it inherited `MarkdownIntegration`'s generic `-p <prompt> --model <model> --output-format json`. Forge accepts `-p/--prompt`, but `--model` and `--output-format` do not exist in its CLI, so every workflow `command:`/`prompt:` step targeting Forge aborted at argument parsing with `error: unexpected argument '--model' found` (exit 2) before the agent ever ran. Unlike the Amp case, both inherited extras are invalid, so both dispatch paths broke: `stream=False` appends `--output-format json`, while `stream=True` still appends `--model` whenever a model is configured. The only surviving combination was `stream=True` with no model, which reduces to plain `forge -p <prompt>`. `model` is deliberately dropped rather than remapped: Forge exposes no model-selection flag. Model choice is a persisted setting (`forge config set model`), and `--agent` takes an agent ID, not a model identifier, so forwarding the caller's model onto it would silently select the wrong thing. `output_json` is dropped for the same reason: Forge's machine-readable `--porcelain` exists only on certain subcommands, not on the top-level prompt invocation. Extra args from `SPECKIT_INTEGRATION_FORGE_EXTRA_ARGS` are applied before `-p`, matching the opencode / goose / codex ordering; Forge parses its global flags ahead of the prompt flag. Same fix shape as the one-off overrides for Amp (github#4581), opencode (github#2409) and goose (github#3781). Part of the audit in github#2416. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Author
|
Follow-up on the Testing section: I now have a Linux environment for this repo, so the Windows caveat above is resolved. Full suite on this branch: Ubuntu 26.04.1 LTS, Python 3.14.4, PowerShell 7.6.6, The failures reported earlier were symlink tests aborting in fixture setup with This comment was drafted with Claude Code (Claude Opus 5); I ran the suite and verified the numbers on my own machine. |
This branch has not been deployed
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.
Description
Fixes #4666. Part of the audit in #2416.
ForgeIntegrationnever overrodebuild_exec_args(), so it inherited the generic implementation fromMarkdownIntegration(base.py), which emits-p <prompt> --model <model> --output-format json. Forge accepts-p/--prompt, but the other two flags do not exist in its CLI, so every workflowcommand:/prompt:step targeting Forge aborted at argument parsing before the agent ran:Unlike the Amp case (#4581), both inherited extras are invalid, so both dispatch paths broke:
stream=False(output_json=True) appends--output-format json→ exit 2.stream=True(the default) drops--output-formatbut still appends--model <model>whenever a model is configured → exit 2.The only surviving combination was
stream=Truewith no model, which reduces to plainforge -p <prompt>.This keeps
-pand drops the two invalid flags:Verified against
forge --help(v2.13.21):-p <prompt>/speckit-…slash invocations--model <model>--output-format json--porcelainis subcommand-onlyOn
model: it is deliberately dropped rather than remapped. Forge exposes no model-selection flag — model choice is a persisted setting (forge config set model <provider> <model>, or interactiveforge select model).--agent <AGENT>takes an agent ID, not a model identifier, so forwarding the caller's model onto it would silently select the wrong thing. This is the one judgement call in the diff; happy to change it if maintainers prefer a different mapping.On flag order:
SPECKIT_INTEGRATION_FORGE_EXTRA_ARGSis applied before-p, matching the opencode / goose / codex ordering. Confirmed Forge parses its global flags ahead of the prompt flag —forge --verbose -p "x" --bogusrejects only--bogus.Same fix shape as the existing one-off overrides for Amp (#4581), opencode (#2409) and goose (#3781); follows the
AmpIntegration.build_exec_args()pattern.Testing
uv run specify --helpuv sync && uv run pytestAdded 4 regression tests to
tests/integrations/test_integration_forge.py, which previously had nobuild_exec_argscoverage at all.Red-green verified. With the source change reverted but the tests kept, all 4 fail; with the fix restored, all 4 pass:
Worth noting one of these caught a real trap during development: a first draft of the "uses
-p" test usedoutput_json=Falsewith no model, which is exactly the one combination that already passes onmain. It was rewritten to assert the real dispatch shape (model set,output_json=True).End-to-end against the real Forge CLI (
npx -y @antinomyhq/forge@latest, v2.13.21) on Windows 11:Only the probe flag is rejected, confirming the generated argv itself parses clean.
Targeted suites on this branch:
tests/integrations/test_integration_forge.py— 30 passedtests/integrations/test_extra_args.py,test_base.py,test_integration_base_markdown.py(the shared machinery this override calls into) plustests/test_agent_config_consistency.py— 177 passed, 1 skippedtests/integrations— 17 failed, 2634 passed, 14 skipped. All 17 failures are symlink tests that abort in fixture setup withOSError: [WinError 1314] A required privilege is not held by the client, because this Windows account has no symlink-creation privilege. None of them is a Forge test, and collection is 2661 onmainversus 2665 here — exactly the four tests added, both figures measured. CI is the authority on pass/fail rather than my machine.ruffreports the same 10 pre-existing findings on these two files before and after the change; this diff introduces none.AI Disclosure
AI disclosure: Agent/tool: Claude Code (interactive CLI session). Model: Claude Opus 5, 1M-token context window, high reasoning effort. Mode: human-supervised, not autonomous — I reviewed the diff and the tests line by line, approved each step, and ran the quoted commands on my own machine. Extent: code generation (the
build_exec_argsoverride and the four regression tests) plus drafting of this description, the linked issue #4666, and any comments I post on this PR. The Forge flag surface was verified againstforge --helpfrom the installed binary (@antinomyhq/forgev2.13.21) and against the upstream clapClistruct, not from model recall.