Skip to content

fix(bundler): normalize the recorded integration key like the canonical reader - #4541

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/bundler-active-integration-clean-key
Open

fix(bundler): normalize the recorded integration key like the canonical reader#4541
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/bundler-active-integration-clean-key

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Problem

active_integration carries a comment saying it matches the canonical reader in integration_state:

default_integration first, matching the canonical reader in integration_state

But that reader normalizes every value through clean_integration_key:

def clean_integration_key(key: Any) -> str | None:
    """Return a stripped integration key, or None for empty/non-string values."""
    if not isinstance(key, str) or not key.strip():
        return None
    return key.strip()

while active_integration only checked:

if isinstance(value, str) and value:
    return value

A whitespace-only key is truthy, so it was returned as a real integration — and, being non-None, it also suppressed the "Returns None when it cannot be determined" fallback the docstring promises. A padded key was returned verbatim and matches no registered integration.

Reproduction on current main (c173bf1)

recorded='copilot'      -> 'copilot'       canonical='copilot'
recorded='  copilot  '  -> '  copilot  '   canonical='copilot'    <-- DIVERGES
recorded='   '          -> '   '           canonical=None         <-- DIVERGES
recorded='\t\n'         -> '\t\n'          canonical=None         <-- DIVERGES
recorded=''             -> None            canonical=None

.specify/integration.json is a plain JSON file, so a hand-edited or externally-written value reaches this directly.

Fix

Delegate to the existing helper instead of re-implementing the check, so the two readers cannot drift apart again:

return clean_integration_key(value)

After the fix all eight shapes — including None, an int and a list — agree with the canonical reader.

Verification

  • Fail-before / pass-after: 3 new-vs-baseline failures with the source reverted to upstream/main51 passed with the fix.
  • Parametrized over seven recorded shapes, each asserted against the value the canonical reader produces, so the test pins agreement rather than a hand-copied expectation.
  • Import checked for cycles (bundler.lib.projectintegration_state) by importing both directly.
  • uvx ruff@0.15.0 check src tests → clean

Behaviour change, disclosed: a padded key now resolves to the stripped key (which is what actually matches a registered integration), and a whitespace-only key now resolves to None — i.e. "not determinable", letting the documented fallback run instead of proceeding with an unusable key. A plain recorded key is unchanged.


Written with assistance from Claude Code. Bug found, reproduced, and verified by me on current main.

🤖 Generated with Claude Code

…al reader

`active_integration`'s own comment says it matches the canonical reader in
`integration_state`, but that reader runs every value through
`clean_integration_key` while this one only checked:

    if isinstance(value, str) and value:
        return value

A whitespace-only key is truthy, so it was returned as a real integration --
and, being non-None, it also suppressed the "not determinable" fallback the
docstring promises. A padded key was returned verbatim and matches no
registered integration.

Reproduced on main:

    recorded='copilot'      -> 'copilot'       canonical='copilot'
    recorded='  copilot  '  -> '  copilot  '   canonical='copilot'    DIVERGES
    recorded='   '          -> '   '           canonical=None         DIVERGES
    recorded='\t\n'         -> '\t\n'          canonical=None         DIVERGES

Now delegates to `clean_integration_key` rather than re-implementing the
check, so the two readers cannot drift again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jawwad-ali
jawwad-ali requested a review from mnriem as a code owner September 11, 2026 17:53
@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

2 participants