Repository navigation
fix(skills): align Mural doctor and auth status on usable sessions - #3179
Conversation
- add store-only _profile_has_usable_session; login overwrite guard unchanged - auth status reports backend_refresh_token apart from authenticated - doctor and dispatch scope gate follow the active profile - remove unused _op_auth_status and allowlist auth_status as CLI-only Closes #3178 🔐 - Generated by Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔐 - Generated by Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🧪 - Generated by Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3179 +/- ##
==========================================
- Coverage 86.61% 81.90% -4.71%
==========================================
Files 87 125 +38
Lines 11554 17625 +6071
Branches 0 63 +63
==========================================
+ Hits 10007 14435 +4428
- Misses 1547 3179 +1632
- Partials 0 11 +11
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Eval Execution✅ Status: Passed
|
There was a problem hiding this comment.
Great work splitting "usable session" from the login overwrite guard. The reasoning in the description is clear, the parity matrix is thorough, and moving the scope gate to the active profile is a good security improvement too. Two comments for you to look at and one housekeeping: Plugin Validation failed on a PSGallery Pester install and needs a re-run. Otherwise looks great and approved
…-session-alignment
🔐 - Generated by Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📝 - Generated by Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pull Request
Description
mural doctorandmural auth statusnow agree on whether a profile is logged in. Areadyverdict from doctor now means Mural commands can actually authenticate. The login overwrite protection added in #3129 is unchanged.#3129 made login and status share one session check. That check counts a
MURAL_REFRESH_TOKENstored in the keyring or credential file, which is right for blocking an accidental login overwrite. Nothing in the request path ever reads that backend token, though; requests authenticate and refresh only from the token-store record. Reusing the same check in doctor, as #3178 originally proposed, would have reportedreadyfor a state in which every command fails with "run auth login first". This PR therefore separates the two questions.Session checks
_profile_has_usable_sessionreads only the token store. It returns true when the active profile's record passes_select_profilevalidation and has an access or refresh token. It never reads the keyring or credential file and never raises._profile_has_session, the login overwrite guard, andauth loginbehavior are unchanged.mural auth statusauthenticatednow comes from the usable-session check.backend_refresh_token, which reports a backend refresh token separately. An unreadable credential file reportsfalseinstead of raising.authenticated: falsewithbackend_refresh_token: truefor a backend-only refresh token.mural doctor--profile, thenMURAL_PROFILE, then the profile chosen bymural auth use, thendefault. Previously it ignored themural auth usechoice.needs_login.Scope check before each command
_require_scoperesolves the same active profile whenmain()calls it. Previouslymain()checkeddefaultwhile the request used the active profile.--profileon a scoped command now exits 1 with a validation error instead of 77 as a scope denial. A test pins this.Cleanup
_op_auth_status, which hard-codedauthenticated: True, along with its package re-export and eight imports that became unused. The handler parity test listsauth_statusas CLI-only.Documentation
SECURITY.mdB3 describes the overwrite guard and the usable-session check separately. The B2 elevation-of-privilege bullet names the active-profile precedence.docs/security/security-model.mdregistry row is updated.SKILL.mddocuments the new status field and doctor's profile resolution, and corrects the scope-upgrade command toauth login --write --force; without--force, login keeps an existing session.needs_loginandneeds_scope_upgraderemediation text inreferences/bootstrap.mdmentions--force. Agents that call doctor match verdict tokens rather than this wording.Related Issue(s)
Fixes #3178
Related to #3129
Type of Change
Select all that apply:
Code & Documentation:
Infrastructure & Configuration:
AI Artifacts:
hve-builderand addressed all actionable findings.github/instructions/*.instructions.md).github/prompts/*.prompt.md).github/agents/*.agent.md).github/skills/*/SKILL.md).github/hooks/*/*.json)evals/)Other:
.ps1,.sh,.py)Sample Prompts (for AI Artifact Contributions)
User Request:
"Seed the Method 4 brainstorming board in Mural." In the DT Coach, the agent first runs
mural doctor --require-scope murals:write.Execution Flow:
Doctor inspects local state for the active profile without contacting Mural. If the only stored credential is a refresh token in the keyring or credential file, doctor now returns
needs_login. The agent stops and relays the bootstrap remediation, which tells the operator to rerun login with--forceif login reports existing stored credentials.Output Artifacts:
{"verdict": "needs_login", "required_scopes": ["murals:write"], "granted_scopes": []}mural auth statusfor the same state reports"authenticated": falseand"backend_refresh_token": true.Success Indicators:
Doctor and
auth statusagree, soreadyis followed by commands that authenticate. Aftermural auth use <profile>, doctor evaluates that profile's login state and scopes.For detailed contribution requirements, see:
Testing
New tests:
A parity matrix runs
auth statusand doctor over the same eight local states:Doctor reports
readyexactly when status reportsauthenticated: true. Doctor runs with the keyring and file backends patched to fail if touched.Usable-session helper cases, plus status cases for a keyring-only refresh token, an unreadable credential file, and a malformed record.
Dispatch scope-gate tests:
--profileoverrides the active profile77 == 1when themain()change is revertedCommands run locally on Windows:
uv run pytestin.github/skills/experimental/muralat4d5cbb832: passed, 987 passed and 146 skipped. Cleanmainat313693b66gives 964 passed and 146 skipped.5c1efedf5,uv run pytest tests/test_doctor.py tests/test_handler_shape_parity.py tests/test_widget_create_bulk_dispatch.py tests/test_mural_main.py: passed, 47 passed and 6 skipped.uv run ruff check scripts testsanduv run ruff format --check scripts tests: passed.git diff --checkover the branch diff: passed.npm run validate:skills: passed, 87 skills with 0 errors and 0 warnings.npm run validate:docs: passed.npm run lint:frontmatterandnpm run lint:tables: passed.npx markdownlint-cli2on the changed Markdown files: passed, 0 issues.npm run docs:generate,npm run docs:generate:check, andnpm run lint:asset-docs: passed with no generated diff and 0 errors.npm run spell-check: failed. All 11 issues aregraphifyin unchangedextension/files.npm run lint:md-links: failed on two external links in unchanged files: the OWASP AI guide (404) and a Medium article (403). Links in the changed files pass.npm run validate:localwas not run as an aggregate because it stops on a local, gitignored corpus directory. Its owning component checks were run individually as listed.Checklist
Required Checks
AI Artifact Contributions
hve-builderreview mode to review contributionhve-builderreviewRequired Local Checks
The following local-safe validation commands must pass before merging:
npm run validate:localnpm run validate:docsnpm run spell-checknpm run lint:md-linksSecurity Considerations
No dependency manifests or lockfiles changed.
Additional Notes
auth statusJSON gains one key,backend_refresh_token. Itsauthenticatedvalue changes fromtruetofalsefor a backend-only refresh token, so backwards compatibility is left unchecked for that reason. No other status keys or exit codes changed.--profileon a scoped command now exits 1 with a validation error instead of 77._profile_has_sessionin doctor. This PR intentionally uses a separate usable-session check instead, for the reasons given in the Description.