Conversation
added 4 commits
August 6, 2026 09:45
infra needs the generic log-event callback but may not depend on app (AGENTS.md dependency rule), so the shape moves to core/ports/log-sink. packages/app/src/update re-exports it, keeping the app public surface unchanged. No behavior change.
Two divergent copies existed: packages/infra/src/hub/default-hubs.ts (Amadeus + community, config/default-hubs.json) and the extension's src/config/default-hubs.ts (awesome-copilot + a non-existent community hub, config/defaultHubs.json). The infra module is now the single source of truth for both delivery layers: - add isDefaultHub()/isRecommendedDefaultHub(), comparing type+location case-insensitively and ignoring the git ref - split icon (plain text, CLI) from codicon (VS Code selector) so one config can serve both hosts - move the JSON to packages/infra/config/default-hubs.json (the path the loader reads) and drop the camelCase extension copy - give only the recommended hub recommended: true; two made getRecommendedHub() order-dependent - replace the h.name === 'Amadeus' compare in cli init with the predicate No behavior change beyond the recommended-flag fix.
raw.githubusercontent.com answers 404 — never 401/403 — when the
Authorization header carries a token GitHub rejects, even for public
content. A stale VS Code session therefore made every hub look
non-existent, and the error blamed the hub URL instead of the credential.
- hub-resolver: one authenticated fetch, no anonymous retry. On failure
with a credential attached it diagnoses the token against
api.github.com and throws a RegistryError whose code names the cause
(AUTH.TOKEN_REJECTED / MISSING_SCOPE / SSO_REQUIRED / NO_REPO_ACCESS,
or HUB.FETCH_FAILED), with the verdict as `hint`.
- TokenProvider now returns { token, origin }. Every provider
self-reports (which env var, which VS Code account, which setting), so
provenance can no longer be lost in the composite chain. formatCredential
renders it into the API client's auth context, the hub resolver's warn
line, bundle-download failures, harvest logs and `doctor`.
- The global promptregistry.githubToken setting is applied and marked
origin=setting:promptregistry.githubToken — never silently dropped.
- verifyHubAvailability gains checkHubAvailability, which reports why. A
default hub an account cannot see is logged at info as "not an error"
with no notification; anything else warns and points at the commands.
- New showAuthFailure/showOperationFailure: the failure is always shown,
with the verdict appended and Diagnose / Reset GitHub Token actions.
- New targeted-only promptregistry.diagnoseGitHubAuth: no source sweep, a
constant public control URL, non-interactive session lookup, and
repoStatus === undefined to detect a credential-level verdict. An
integration test asserts it is both registered at activation and
declared in the manifest's commands + commandPalette — a unit test can
see neither, since it mocks `vscode` and never loads package.json.
- Rewrote docs/contributor-guide/architecture/authentication.md.
Carried over from AmadeusITGroup#374: github-token-diagnostics, github-api-client's
auth context, vscode-session-token-provider, github-auth-command's real
forceNewSession. Left behind: the anonymous fallback, the startup token
probe, and the silent global-token drop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
raw.githubusercontent.comanswers 404 — never 401/403 — when theAuthorizationheader carries a token GitHub rejects, even for content that is public and served fine anonymously. A stale VS Code session therefore makes every hub look non-existent, and the error blames the hub URL instead of the credential.This makes every auth failure fail loudly, name the root cause, say which credential was used and where it came from, report scopes and SSO authorization, and offer Diagnose + Reset GitHub Token.
One deliberate exception: an account with no access to the default
Amadeus-xDLC/genai.prompt-registry-confighub — an open-source contributor outside Amadeus — is an expected condition. Clearinfologs, no notification, no error.Important
Depends on #386 and #387, and must merge after both. This branch contains their commits, so the diff shown here includes them until they land. Review those two first; this PR's own change is the ~50 files outside
packages/core/src/ports/log-sink.tsand the default-hubs consolidation.Replaces #374. That PR diagnosed the problem correctly but then worked around it:
hub-resolver.tssilently retried anonymously and served the config, andregistry-manager.tssilently dropped a rejectedpromptregistry.githubToken. Both hide the fault the PR exists to expose.Type of Change
Breaking at the source level only:
core'sTokenProvider.getTokennow returnsResolvedToken { token, origin }instead ofstring. All ~19 in-repo call sites are updated. No user-visible or runtime behavior depends on it, but a downstream consumer of the published@ai-primitives-hub/coreport would need the one-line change.Related Issues
Relates to #374 — supersedes it. Close #374 in favour of this, or reuse its review thread.
Changes Made
Fail loudly (
packages/infra/src/hub/hub-resolver.ts)api.github.com, emit awarnlog event, then throw.RegistryErrorso callers classify on a code instead of matching message text:AUTH.TOKEN_REJECTED(401 on/user),AUTH.MISSING_SCOPE(norepo),AUTH.SSO_REQUIRED(x-github-ssopresent),AUTH.NO_REPO_ACCESS(valid token, repo invisible),HUB.FETCH_FAILED(anonymous, orapi.github.comunreachable). Context carries{ url, repoLocation, status, origin, scopes, sso, login };hintis the verdict.Token origin through the port (
packages/core/src/ports/http.ts)TokenProvider.getTokenreturnsResolvedToken { token, origin }; newTokenOrigin { kind, detail? }with kindsexplicit | setting | vscode-session | env | gh-cli | unknown.EnvTokenProviderdistinguishesGITHUB_TOKENfromGH_TOKEN(it used to collapse them),GhCliTokenProviderreportsgh auth token,StaticTokenProvidertakes an optional origin,VsCodeSessionTokenProviderreports the account label.CompositeTokenProviderpasses the winner through untouched — that loop is where provenance died before.formatCredentialrendersorigin=vscode-session(octocat) token=***<len=40,tail=9c1e>, threaded into the API client's auth context, the hub resolver's warn line and error context,https-bundle-downloader(which threw a bareHTTP <status>with no auth context at all), harvest logs, andai-primitives-hub doctor.opts.explicitToken ? 'explicit' : 'env'inhub-harvester.ts, which mislabelled everygh-CLI andGH_TOKENrun.tokenSourceToOriginmaps harvest's existingTokenSourceunion ontoTokenOriginso logs share one vocabulary.Global token applied, never dropped (
registry-manager.ts)promptregistry.githubTokenis still applied, now markedorigin=setting:promptregistry.githubToken. A rejected setting token produces a loud, origin-labelled failure instead of a silent fallback.Clear Tokenaction (which wroteConfigurationTarget.Global— destructive and out of scope).Expected vs real failure (
packages/app/src/registry/hub-manager.ts,utils/first-run-hub-report.ts)checkHubAvailabilityreturnsHubAvailability { available, reason?, credential?, detail? }mapped from the error code, alongside the existing booleanverifyHubAvailability.reason === 'no-access'andisDefaultHub(reference)→info, no notification, ending in "This is not an error." Anything else →warnplus the credential, the verdict, and a pointer to the two commands. When every default hub fails and all failures are expected, the user gets an information message; one real failure makes it a warning. The picker still offers Custom URL + Skip either way.The user sees the failure (
utils/show-auth-failure.ts)showAuthFailureshows the original message plus the verdict, with Diagnose / Reset GitHub Token / Show Logs.showOperationFailureroutes non-auth errors to a plain notification.returned, soFailed to install bundle: …was never shown and a "healthy" verdict could appear for a failed install.Targeted diagnosis (
commands/diagnose-github-auth-command.ts)GITHUB_SOURCE_TYPESset duplicating adapter knowledge, noRegistryManagerdependency.report.repoStatus === undefined— an explicit fact. fix(infra): credential diagnostics and anonymous fallback for hub resolution #374 inferred it fromreports.length !== locations.length, which mislabels whenever there is exactly one location and a rejected credential.getRecommendedHub()cannot serve here: the recommended hub is private, so "the control failed" would be indistinguishable from "no access".createIfNone: false) — read-only diagnostics must not pop a sign-in modal and then report on a session that was not the one that failed.Testing
Test Coverage
Added
packages/infra/test/hub/hub-resolver.test.ts— six cases: a stale token on public content must reject withAUTH.TOKEN_REJECTED, exactly one hub-config fetch happened (no anonymous retry), theonLogwarn line carries origin + redacted token + verdict and never the token itself, plus one case per remaining code.packages/infra/test/auth/{format-token-origin,static-token-provider}.test.ts— new files;composite-token-provider.test.tsgains a case asserting the winner's origin survives the chain.apps/vscode-extension/test/utils/first-run-hub-report.test.ts(8),test/utils/show-auth-failure.test.ts(12),test/commands/diagnose-github-auth-command.test.ts(7),test/ui/marketplace-view-provider.installFailure.test.ts(2 — a regression guard that the message still starts withFailed to install bundle:).test/suite/integration-scenarios.test.tsasserts the new command is both registered at activation and declared in the manifest'scommands+commandPalette. A unit test can see neither — it mocksvscodeand never loadspackage.json. Verified non-vacuous by breaking each half in turn (renaming theregisterCommandid, then removing the palette entry); each produced the expected failure.Deliberately not carried over: #374's
expectAnonymousFallbackhelper, which asserts the opposite of the intended behavior.Manual Testing Steps
Automated verification (all green):
pnpm --filter "@ai-primitives-hub/*" build— clean.compile,compile-tests— clean.test:unit2221 passing;test:integration8 passing.eslint src test --fixin all five packages — clean, no files rewritten.node -e "require('@ai-primitives-hub/infra')"— the 12 new barrel exports resolve;formatCredentialrenders as documented.Still outstanding — these need a real VS Code session and two GitHub accounts, and I could not run them:
promptregistry.githubTokento garbage → every failure log containsorigin=setting:promptregistry.githubToken, and a public hub fetch fails (no anonymous recovery). Notification offers Diagnose + Reset.ⓘ Hub not available to this accountlines; picker offers Custom URL + Skip.Failed to install bundle:and offers Diagnose + Reset.Tested On
macOS
Windows
Linux
VS Code Stable
VS Code Insiders
VS Code Stable via
@vscode/test-electron(integration suite), not a hand-driven session.Screenshots
None. The three manual scenarios above are where the notification text would be worth capturing.
Checklist
The last box is the blocker: #386 and #387 must merge first.
Self-review went further than reading the diff — two independent review passes (functional completeness, technical debt) were run over the staged change. Both actionable findings were fixed: the anonymous path's error context now uses
formatCredential(undefined)for consistency, and the diagnostics chain is aCompositeTokenProviderrather than an array iterated by hand.Documentation
docs/contributor-guide/architecture/authentication.mdrewritten: keeps the 404-ambiguity explanation, addsorigin=to the auth-context sample, states explicitly that the hub resolver has no anonymous fallback, adds a Token origin table and an Expected: no access to a default hub section with the verbatim log lines and the rule that it isinfo, neverwarn/error, and never a notification. Removed #374's two-shape (targeted vs sweep) table and its claim that a marketplace install launches the diagnosis automatically — it now shows the error with a Diagnose action.Also corrects #374's description: it claimed
describeTokenlogsgho_*** (length 40). The actualredactTokenemits***<len=N,tail=abcd>— length plus the last four characters. These strings now reach user-facing notification text viadescribeError, not only the output channel.Additional Notes
Why a rebuild rather than a revert on
bugfix/auth-issues: that branch's history would have become add-then-remove, and two of its three pieces (the log-sink move, the default-hubs consolidation) belong on their own branches regardless.Behavior change reviewers should weigh: a rejected credential now fails public hub fetches that previously succeeded via the anonymous retry. That is the point — it surfaces a broken credential instead of masking it until the first private hub — but it does mean a user with a stale token who only uses public hubs will newly see an error. They now get an actionable one.
Reviewer Guidelines
Please pay special attention to:
isDefaultHubgating the "expected" path. Too broad a match would silence a genuine failure; the predicate ignores the git ref by design (see refactor: consolidate default hub configuration into infra #387).TokenProviderbecoming a breaking port change. The alternative was an optional side-channel for origin, which loses provenance the moment a caller forgets it. I took the compile-time-enforced route; it costs ~19 mechanical call-site updates.catchblocks aroundresolver.resolve(app/src/registry/hub-manager.ts,load-hub-sources.ts, the extension'shub-manager.ts).RegistryError extends Error, soerror.messageconsumers keep working — but please confirm none of them needed the old bare-Errorshape.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache License 2.0.