{"record":{"id":"72179a5d2a464f1a","repo":"github/spec-kit","slug":"bundle-manifest-bundle-id-targets-integration","errorCode":null,"errorMessage":"Bundle '{manifest.bundle.id}' targets integration '{required}', but this project's active integration is '{active_integration}'. Installing it would conflict; aborting with no changes.","messagePattern":"Bundle '(.+?)' targets integration '(.+?)', but this project's active integration is '(.+?)'\\. Installing it would conflict; aborting with no changes\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/services/resolver.py","lineNumber":94,"sourceCode":"            )\n\n    # FR-019: integration-compatibility — a bundle that pins a different\n    # integration than the project's active one halts (no silent change).\n    #\n    # A blank integration arrives as ``\"\"``, not ``None`` — which is not a usable\n    # integration id but satisfied NEITHER guard below (the first is a truthiness\n    # test, the second an ``is None`` test), so a pinned bundle was silently\n    # adopted: precisely the outcome this guard exists to prevent. Treat blank as\n    # indeterminate, and strip first like the writer\n    # (``integration_state.clean_integration_key``) so a padded value is not\n    # reported as clashing with itself.\n    if active_integration is not None:\n        active_integration = active_integration.strip() or None\n    effective_integration = active_integration\n    if manifest.integration is not None:\n        required = manifest.integration.id\n        if active_integration and required != active_integration:\n            raise BundlerError(\n                f\"Bundle '{manifest.bundle.id}' targets integration '{required}', \"\n                f\"but this project's active integration is '{active_integration}'. \"\n                \"Installing it would conflict; aborting with no changes.\"\n            )\n        if active_integration is None and not integration_explicit:\n            raise BundlerError(\n                f\"Bundle '{manifest.bundle.id}' targets integration '{required}', \"\n                \"but this project's active integration could not be determined \"\n                \"(missing or unreadable .specify/integration.json). Re-run with \"\n                \"'--integration' to confirm the target, or repair the project \"\n                \"before installing.\"\n            )\n        effective_integration = required\n\n    warnings: list[str] = []\n    if manifest.requires.tools:\n        warnings.append(\n            \"Requires external tools: \" + \", \".join(manifest.requires.tools)","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/services/resolver.py#L76-L112","documentation":"FR-019 integration-clash guard in resolve_install_plan(): the bundle pins an integration (manifest.integration.id) that differs from the project's active integration (from .specify/integration.json, stripped like clean_integration_key). Installing would silently switch agents, so resolution aborts with no changes. Note the active value is normalized first: blank strings become None, padded values are stripped.","triggerScenarios":"resolve_install_plan(manifest, active_integration='claude', ...) where the bundle.yml declares integration: {id: cursor-agent}; via 'specify bundle install <bundle>' in a project initialized with a different agent than the bundle targets.","commonSituations":"Team shares a bundle authored for Claude Code but a teammate's project was initialized with --integration gemini; mixing bundles authored for different agents in one project.","solutions":["Install with an explicit override matching the bundle's target only if intentional: re-initialize or pass the pinned integration (specify bundle install <id> --integration <id> is only accepted when it matches; otherwise switch the project).","Switch the project's active integration to the bundle's target (re-run specify init --integration <id>) if you want that bundle.","Use a variant of the bundle without an integration pin (manifest.integration: null) or one targeting your active integration.","If you author the bundle, remove the integration pin when the content is agent-neutral."],"exampleFix":"# before: project initialized with --integration claude,\n# bundle.yml pins:\nintegration:\n  id: cursor-agent\n\n# after: pin to the project's integration, or drop the pin\nintegration:\n  id: claude\n# or remove the integration block entirely","handlingStrategy":"validation","validationCode":"from specify_cli.bundler.services.integration_state import active_integration\n\ndetected = active_integration(project_root)\nif manifest.integration is not None and detected and manifest.integration.id != detected:\n    print(f\"Skip: bundle targets {manifest.integration.id}, project uses {detected}\")","typeGuard":"def bundle_matches_project(manifest, active: str | None) -> bool:\n    return manifest.integration is None or active is None or manifest.integration.id == active","tryCatchPattern":"try:\n    resolve_install_plan(manifest, active_integration=detected, ...)\nexcept BundlerError as exc:\n    if \"Installing it would conflict\" in str(exc):\n        # either switch project integration or pick another bundle\n        ...","preventionTips":["Check .specify/integration.json before installing agent-pinned bundles.","Publish agent-neutral bundles without an integration pin when possible.","Standardize the team on one integration before sharing bundles."],"tags":["bundler","integration","compatibility","guard"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}