fix(bundler): treat a blank active integration as indeterminate in the FR-019 guard - #3886
Open
jawwad-ali wants to merge 1 commit into
Open
fix(bundler): treat a blank active integration as indeterminate in the FR-019 guard#3886jawwad-ali wants to merge 1 commit into
jawwad-ali wants to merge 1 commit into
Conversation
…-019
`resolve_install_plan`'s two FR-019 guards are a truthiness test and an
`is None` test:
if active_integration and required != active_integration: # clash
if active_integration is None and not integration_explicit: # indeterminate
An empty string satisfies neither, so it falls through to
`effective_integration = required` and the bundle's pinned integration is
silently adopted — the exact outcome the docstring says the guard prevents
("resolution fails instead of silently adopting the bundle's required
integration").
active=None -> BundlerError: ... could not be determined
active='' (blank) -> effective_integration='copilot' <-- silent adopt
active='claude' -> BundlerError: ... targets integration 'copilot'
Normalise a blank value to None before the guards, and strip first to
match the writer (`integration_state.clean_integration_key`, which returns
`None` for empty/whitespace and strips otherwise) so a padded value is not
reported as clashing with its own unpadded form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes FR-019 enforcement by treating blank or whitespace-only active integrations as indeterminate.
Changes:
- Normalizes active integration values before compatibility checks.
- Adds coverage for blank, whitespace, and padded integration values.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/bundler/services/resolver.py |
Normalizes blank and padded integration IDs. |
tests/unit/test_bundler_resolver.py |
Tests blank rejection and padded-value matching. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
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.
Problem
resolve_install_planinsrc/specify_cli/bundler/services/resolver.pyenforces FR-019 with two guards:An empty string satisfies neither — it is falsy, so the first is skipped; it is not
None, so the second is skipped. Execution falls through toeffective_integration = required, silently adopting the bundle's pinned integration.That is precisely what the function's own docstring says it prevents:
Reproduction on current
main(81bf741)Same manifest, pinned to
copilot:Both a known-different integration and an unknown one halt. Only the blank one silently proceeds — and it proceeds by adopting a different integration than the project has, which is the one outcome FR-019 exists to block.
Fix
Normalise blank to
Nonebefore the guards, stripping first:The strip matches the writer side.
integration_state.clean_integration_keyis the canonical normaliser:Without stripping,
" claude "would be reported as clashing withclaude— i.e. with itself. A new test pins that.No breaking change.
Noneand any non-blank value behave exactly as before. The only inputs whose behaviour changes are blank/whitespace ones, which move from "silently adopt a foreign integration" to the existingcould not be determinederror — the same pathNonealready takes.--integrationwith an explicit value still overrides, unchanged.Verification
srcand pass with the fix. File: 4 failed → 12 passed.tests/unit: failure set identical to the clean-mainbaseline captured on81bf741(2 pre-existing in scope).uvx ruff@0.15.0 check src tests→ cleanTests sit beside
test_pinned_integration_with_indeterminate_active_fails, the existing FR-019 guard test asserting the samecould not be determinedmessage.Written with assistance from Claude Code. Bug found, reproduced, and verified by me on current
main.