Skip to content

fix(github-issues): base the auth row on the active provider, not the raw gh flag - #156

Closed
eneskirca wants to merge 1 commit into
mainfrom
fix/github-auth-followup
Closed

fix(github-issues): base the auth row on the active provider, not the raw gh flag#156
eneskirca wants to merge 1 commit into
mainfrom
fix/github-auth-followup

Conversation

@eneskirca

@eneskirca eneskirca commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Follow-up to #115, addressing a review (Opus + a second reviewer, corroborating).

What was wrong

#115 relocated the token controls correctly, but the new copy keyed off auth.ghAuthenticated, which lies in two common states:

  1. High — an unapproved project reads as "signed out." src/core/github/host.ts masks the auth block to {ghAuthenticated:false, activeProvider:null} until the repository is approved (it won't resolve credentials before consent). Approval happens right above this row in the setup flow, so during setup a signed-in user saw "GitHub CLI is not signed in. Run gh auth login…" with the token field front and center — the exact noise fix(github-issues): hide token controls when the GitHub CLI is signed in #115 set out to remove, plus a wrong instruction.
  2. Medium — ghAuthenticated ≠ what authenticates a request. Requests authenticate via activeProvider. With provider:'token' and no token saved, activeProvider is null (requests fail), yet the section still rendered the green "✓ Signed in via GitHub CLI."
  3. Medium — the Advanced summary over-promised. "use a personal access token instead" implied saving a token switches auth off the CLI; under the default auto provider it does not (auto keeps preferring the signed-in CLI).

Fix

  • The row's decision is now a pure, unit-tested describeGitHubAuth (src/renderer/lib/githubAuthView.ts) over {approved, provider, activeProvider, tokenPresent} → one of unapproved | gh | token | need-gh | need-token. tokenFieldIsPrimary decides whether the token field is foregrounded (it's the only way in) or tucked into Advanced.
    • unapproved → neutral "Approve this repository above to check GitHub authentication." — never "signed out".
    • gh → the green check, gated on activeProvider === 'gh'; "No token needed" dropped when gh-pinned; a "saved token kept as a fallback" note when one exists.
    • token"✓ Authenticated with a saved personal access token."
    • need-gh / need-token → correct, provider-aware guidance (a token hint only when it would actually be consulted).
  • Honest Advanced summary: "Advanced — authentication provider & token".
  • A "Check again" link in the need-gh state (status() bypasses the 30 s credential cache, so it reflects a just-run gh auth login immediately).
  • :focus-visible outline on the disclosure summary.

Testing

  • New githubAuthView.test.ts — 8 cases pinning every branch, incl. the two regressions above (unapproved-is-not-signed-out; gh-up-but-token-pinned-missing is need-token, not a green check).
  • styles.theme.test.ts guard passes (--accent is a defined token).
  • npm run typecheck + npm run build clean.
  • Not browser-verified (Playwright MCP unavailable this session) — visual pass owed on Mac.

🤖 Generated with Claude Code

… raw gh flag

Follow-up to #115. The relocation was right, but the new copy keyed off
`ghAuthenticated`, which lies in two common states:

- **Unapproved project reads as "signed out."** The host masks the auth block
  to `{ghAuthenticated:false, activeProvider:null}` until the repository is
  approved (it won't resolve credentials before consent), and approval comes
  right above this row in setup — so a signed-in user briefly saw "GitHub CLI
  is not signed in. Run gh auth login" with the token field front and center,
  the exact noise #115 removed. Unapproved is now its own neutral state.
- **gh up + provider pinned to a missing token showed a green "✓ signed in."**
  What authenticates a request is `activeProvider`, not `ghAuthenticated`; with
  `provider:'token'` and no token saved, `activeProvider` is null and requests
  fail, so the ✓ was wrong.

The row's decision is now a pure, unit-tested `describeGitHubAuth`
(`githubAuthView.ts`) over `{approved, provider, activeProvider, tokenPresent}`
→ one of `unapproved | gh | token | need-gh | need-token`, and
`tokenFieldIsPrimary` decides whether the token field is foregrounded or tucked
into Advanced. Also: honest Advanced summary ("provider & token", not "use a
token instead" — under `auto`, saving a token does NOT switch auth off gh), a
"Check again" link after `gh auth login` (status() bypasses the credential
cache, so it's accurate), a saved-token fallback note, and a `:focus-visible`
outline on the disclosure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@eneskirca

Copy link
Copy Markdown
Owner Author

Heads-up: this collided with a parallel effort on the same two root causes — #165 was built independently against the same review findings and has just been merged, so GitHubIssuesSection.tsx on main already carries the fix.

Coverage check I ran before choosing (not a judgement on the code, which is good):

state #156 #165
unapproved project must read as unknown, not signed-out
✓ only when activeProvider === 'gh'
pinned provider stated inline
saved token surfaced as a fallback
activeProvider === 'token' state
failed auth read → "not checked yet"
"Check again" after gh auth login
settings search opens the collapsed Advanced

So #165 was a superset behaviourally, which is why it went in.

What this PR still has that main does not, and I think it is the better shape: the pure describeGitHubAuth in renderer/lib/ with its own unit tests. That matches the house pattern (noteLink.planBridges, pendingLaunch.resolveAfter, verifyPanel) and it is exactly the kind of derivation that rots when it lives inline in a component — the bug being fixed here was one flag driving five sentences, which a pure function with a discriminated union makes structurally hard to reintroduce. If you want to re-land just that part on top of main — the module plus its tests, with the component reduced to rendering the union — I would take it.

Apologies for the duplicated work; that is on the coordination, not on you.

@eneskirca

Copy link
Copy Markdown
Owner Author

Superseded by main — recommend closing.

Commit 83823ddf ("fix(github-issues): describe what actually authenticates, not one flag") landed the same fix on main independently, and went a little further:

  • same core correction — decide off activeProvider, not the raw ghAuthenticated flag
  • adds selectedProvider and tokenIsAside so the token control moves into Advanced whenever a token cannot be what signs the user in
  • distinguishes unknown (!auth, the status read has not landed) from signed-out, which is the same bug this branch's unapproved state was fixing
  • additionally handles searching — a control the user searched for is not left hidden behind a collapsed disclosure, which this branch does not do
  • ships tests in GitHubIssuesSection.test.tsx

The merge conflict here is not mechanical: both sides rewrote the same block, and main's version is the newer and more complete one.

One thing worth salvaging before closing: this branch extracted the branch logic into src/renderer/lib/githubAuthView.ts as a pure, unit-tested function (describeGitHubAuth / tokenFieldIsPrimary, 64 lines of tests), whereas main keeps the equivalent logic inline in the component and tests it through the rendered section. If the extraction is still wanted, it is a small follow-up against main's shape rather than a rebase of this branch.

Also for the record: the red quality check on this PR is the known license.test.ts clock-rollback flake, not anything in the diff.

Your call on the extraction — leaving this open rather than closing someone's branch on their behalf.

@eneskirca

Copy link
Copy Markdown
Owner Author

Closing as superseded — every fix in this PR is on main, implemented inline, and tested.

The three problems you identified:

  1. Unapproved reads as "signed out"GitHubIssuesSection.tsx:90 authBlockFor asks for the project-independent block precisely when the project is not approved, with a comment stating the same diagnosis ('approval comes AFTER authentication in the visible flow').
  2. ghAuthenticated ≠ what authenticates a request → main reads activeProvider, and its comment says it in your words: "ghAuthenticated alone lies in both directions: pinned to token-only it can be true while nothing authenticates, and pinned to gh-only a saved token is inert however present it is."
  3. Token control placementtokenIsAside does what your tokenFieldIsPrimary did.

And the coverage landed too — GitHubIssuesSection.test.tsx pins every state this PR enumerated, at the component level rather than through an extracted helper:

  • reads the unmasked auth block for a project that is not approved yet
  • falls back to "not checked yet" rather than "signed out" when the auth read fails
  • does not claim the CLI signs the user in when authentication is pinned to a token
  • does not offer an inert token when authentication is pinned to the GitHub CLI
  • says a saved token is kept as a fallback instead of pretending none exists
  • re-reads the status when the user checks again after signing in (your "Check again" link)

The one thing this branch still has that main doesn't is the extractiondescribeGitHubAuth as a pure, separately unit-tested module. That's a fair idea on its own, but the branch is 12 days stale and CONFLICTING in both GitHubIssuesSection.tsx and styles.css against the very code that replaced it, so it would be a rewrite rather than a rebase. If the extraction is still wanted it's a fresh, small PR against main's current shape.

(For the record, the CI red on this branch was never yours — it's the known license.test.ts clock-rollback flake.)

@eneskirca eneskirca closed this Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant