fix(mural): fallback to file backend when keyring is empty#2135
fix(mural): fallback to file backend when keyring is empty#2135akazlow wants to merge 7 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2135 +/- ##
==========================================
- Coverage 80.52% 78.22% -2.31%
==========================================
Files 128 87 -41
Lines 19274 13932 -5342
Branches 12 0 -12
==========================================
- Hits 15521 10898 -4623
+ Misses 3750 3034 -716
+ Partials 3 0 -3
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Review SummaryThis fix addresses credential backend resolution when the keyring backend is selected but lacks usable secrets. Key Changes
Impact
Verification Checklist
|
3042220 to
3899995
Compare
katriendg
left a comment
There was a problem hiding this comment.
Thanks for filing both the issue (#1942) and this fix, @akazlow — clear, well-scoped report and a tidy, minimal change. I validated it locally: an available-but-empty keyring plus a populated credential file now resolves to FileBackend and emits the one-time fallback warning, matching all five steps of your suggested fix. The existing test_credential_storage.py suite (38 tests) still passes and ruff is clean. 🎉
Two process asks before we go further:
- Please update the PR description to follow the repo's pull request template — filling in Description, Related Issue, Type of Change, Testing, and the Checklist keeps reviews consistent. (
Fixes #1942is already there — nice.) - In that checklist, please tick the CI checks that apply to this change and leave the rest as N/A. This diff only touches a Python script in the
muralskill, so the markdown / docs / PowerShell checks don't apply; the relevant ones are skill structure validation (npm run validate:skills), Python lint (npm run lint:py), and the skill's own pytest suite, plus Tests added and Documentation updated under Required Checks (see inline comments).
I've left a few inline comments — one test gap and a docstring update I'd like to see before merge, plus a couple of optional / consistency notes. Thanks again for contributing!
| if value: | ||
| keyring_populated = True | ||
| break | ||
| if not keyring_populated: |
There was a problem hiding this comment.
Regression test (please address): This new fallback path has no test. Could you add one to the TestResolveBackend class in tests/test_credential_storage.py asserting that, in auto mode, an available-but-empty keyring plus a populated credential file resolves to FileBackend and emits exactly one fallback warning (deduped per profile per process, mirroring test_auto_fallback_warn_dedupes_per_profile_per_process)? The existing test_auto_returns_keyring_when_available only covers the both-empty case, so this path is currently untested.
| elif selector == "auto": | ||
| try: | ||
| selected = KeyringBackend() | ||
| service = _service_name_for(profile) |
There was a problem hiding this comment.
Docstring update (please address): The resolve_backend docstring above still describes auto fallback as happening only "when _KeyringUnavailable is raised." This PR adds a second trigger (keyring available but empty → file backend when the file is populated). Please extend the docstring to document the new path and its one-shot warning so the contract stays accurate.
| if not keyring_populated: | ||
| file_backend = FileBackend(file_path) | ||
| if file_path.exists(): | ||
| _check_credential_file_perms(file_path, os.environ) |
There was a problem hiding this comment.
Perms-check consistency (your call, non-blocking): This path runs _check_credential_file_perms() at resolve time, which can raise MuralError on POSIX for a bad mode/owner. The keyring-unavailable fallback just below constructs FileBackend(file_path) without a resolve-time perms check. On Windows this is a no-op, but on POSIX the empty-keyring fallback is now stricter than the unavailable-keyring one. Worth deciding whether to apply the check to both paths or neither, for symmetry.
| if file_path.exists(): | ||
| _check_credential_file_perms(file_path, os.environ) | ||
| file_entries = file_backend._read_all() | ||
| file_populated = any( |
There was a problem hiding this comment.
Optional refactor: These keyring / file "is populated" probes duplicate logic already in _maybe_warn_concurrent_state. A small shared helper (e.g. _backend_has_credentials(backend, service)) would centralize it and reduce drift risk if the key set or probing logic changes later. Purely optional.
| service = _service_name_for(profile) | ||
| keyring_populated = False | ||
| for key in _KNOWN_CREDENTIAL_KEYS: | ||
| value = selected.get(service, key) |
There was a problem hiding this comment.
Info, no action needed: Because this .get() runs inside the outer try, a keyring read error after successful construction is caught by except _KeyringUnavailable and reported as "unavailable… falling back" rather than "empty." Reasonable behavior — just flagging that the message can read slightly off in that edge case.
Pull Request
Description
Fixes backend resolution when auto mode selects keyring but keyring contains no usable credentials.
In that case, when the credential file is populated, resolution now falls back to file backend with a one-shot warning so authentication can proceed.
Related Issue(s)
Fixes #1942
Type of Change
Select all that apply:
Code & Documentation:
Infrastructure & Configuration:
AI Artifacts:
prompt-builderagent and addressed all feedback.github/instructions/*.instructions.md).github/prompts/*.prompt.md).github/agents/*.agent.md).github/skills/*/SKILL.md)Other:
.ps1,.sh,.py)Testing
.github/skills/experimental/mural/tests/test_credential_storage.pyto cover keyring-available-but-empty fallback behavior.pytestin the active.venv; CI remains the source of truth for full test execution.Checklist
Required Checks
AI Artifact Contributions
/prompt-analyzeto review contributionprompt-builderreviewRequired Automated Checks
The following validation commands must pass before merging:
npm run lint:mdnpm run spell-checknpm run lint:frontmatternpm run validate:skillsnpm run lint:md-linksnpm run lint:psnpm run plugin:generatenpm run docs:testSecurity Considerations
Additional Notes