aim/.plans/011-appimagehub-and-query-fallback/2026-03-21-appimagehub-and-query-fallback-design.md

8.7 KiB

AppImageHub Provider And Query Fallback Design

Summary

This change adds AppImageHub as a first-class source and search provider, removes the unused custom-json stub, and changes positional aim <query> so it falls back to search when no direct installable match is available across providers. The direct-input contract stays deterministic: explicit provider URLs and shorthands still install immediately, while plain-name queries become a search-first discovery experience only after strict direct resolution fails.

Goals

  • Add AppImageHub as a supported provider for direct resolution and search.
  • Accept direct AppImageHub inputs as either item URLs or appimagehub/<id> shorthand.
  • Preserve strict direct resolution semantics for provider-specific inputs.
  • Make positional aim <query> fall back to cross-provider search when direct resolution is unsupported or yields no installable artifact.
  • Remove the custom-json adapter stub and its registration/tests.
  • Keep provider identity stable in the registry by using AppImageHub numeric item IDs as canonical locators.

Non-Goals

  • No AppImageHub slug-based direct input format.
  • No new interactive chooser or auto-install of fuzzy search winners in this slice.
  • No redesign of aim search output beyond adding AppImageHub-backed results.
  • No machine-readable provider metadata output beyond what current domain models already expose.
  • No provider-agnostic alias layer for AppImageHub names.

Approaches

This is the approved design. Direct AppImageHub inputs are limited to the observable stable forms https://www.appimagehub.com/p/<id> and appimagehub/<id>. Plain names are handled by the cross-provider search path, which can return install-ready appimagehub/<id> install queries. This keeps direct installs deterministic and avoids inventing a slug model the service does not clearly expose.

Option 2: Title-based direct shorthand

This would accept human-readable AppImageHub names as if they were stable direct locators. It looks cleaner, but it collapses search and direct resolution into one fuzzy layer and makes title normalization, collisions, and title drift part of the persistent provider contract.

Option 3: Provider alias registry

This would map friendly names to canonical AppImageHub IDs inside aim. It could improve CLI ergonomics later, but it adds storage, collision handling, update drift, and debugging overhead that are not justified for the initial provider integration.

Approved Design

Public Command Behavior

Positional aim <query> becomes a two-phase command:

  1. attempt strict direct resolution using existing provider/source rules
  2. if direct resolution yields an installable artifact, continue through the current add/install flow
  3. if direct resolution is unsupported or resolves to a provider item with no installable artifact, fall back to cross-provider search

This preserves deterministic direct installs for URLs and provider shorthands while turning plain-name queries such as aim firefox into a discovery flow instead of a dead-end error.

The fallback rule is intentionally command-level policy, not a change to resolve_query(...). Strict source classification should remain strict. The new behavior belongs in a higher-level orchestration layer that decides whether the positional command is an install or a search.

AppImageHub Input Model

AppImageHub direct inputs are:

  • https://www.appimagehub.com/p/<id>
  • http://www.appimagehub.com/p/<id>
  • appimagehub/<id>

The canonical provider identity is the numeric AppImageHub item ID. That ID should be stored as the canonical locator in the resolved source so the registry remains stable even if the user-facing title changes.

The source model gains:

  • SourceKind::AppImageHub
  • a SourceInputKind for AppImageHub URLs
  • a SourceInputKind for AppImageHub shorthand
  • NormalizedSourceKind::AppImageHub

The visible locator can remain the item page URL for readability, but provider matching and search/install identity should rely on the canonical ID.

Provider Architecture

AppImageHub should be implemented as a real source adapter plus a real search provider.

The source adapter is responsible for:

  • normalizing URL and shorthand inputs into a SourceRef
  • resolving an AppImageHub item into the latest installable AppImage artifact
  • returning a provider-specific no-installable-artifact outcome when the item exists but does not expose a usable AppImage asset

The search provider is responsible for:

  • searching AppImageHub content by title or provider-supported query text
  • mapping results into the existing SearchResult model
  • returning install-ready queries as appimagehub/<id>

This keeps AppImageHub aligned with the existing GitHub/Search provider split and avoids tunneling it through generic direct URLs.

Positional Query Fallback Flow

Add a new orchestration layer for positional aim <query> that makes the install-versus-search decision explicit. Conceptually:

  • attempt build-add-plan
  • if a direct install plan is produced, continue with the current install path
  • if the add-plan path fails with unsupported query or no installable artifact, build search results instead
  • if search finds hits, render search results instead of returning the add error
  • if search finds nothing, render the normal empty search state instead of unsupported source query

This should be represented as a dedicated dispatch/core decision rather than scattered error remapping in CLI display code.

Error Handling

The important distinction is between provider resolution and user-facing command behavior:

  • strict direct resolution can still return Unsupported
  • AppImageHub resolution can still return NoInstallableArtifact
  • positional aim <query> treats those outcomes as search-fallback triggers
  • explicit aim search <query> skips direct resolution entirely and always searches

Provider failures during search should continue to use the existing warning model. If all providers fail and no results are available, search should still surface provider failure warnings as today.

Malformed direct AppImageHub inputs should remain malformed direct inputs. They should not silently become provider-specific direct matches. If the text is not a valid direct AppImageHub source, it should either be an unsupported direct query or a plain search term depending on the command path.

Search Result Semantics

AppImageHub search hits should populate:

  • provider_id = "appimagehub"
  • display_name = <item title>
  • source_locator = <item page url>
  • install_query = appimagehub/<id>
  • canonical_locator = <id>

Installed-status annotation should treat AppImageHub IDs the same way GitHub currently treats canonical locators, so installed AppImageHub apps can show as installed or update-available inside search results.

Removal Of custom-json

custom-json is a stub with no supported query or resolution behavior. It should be removed outright:

  • delete the adapter module
  • remove it from adapter registration
  • remove it from smoke/contract coverage where it appears as an expected adapter kind

This slice should not replace it with another placeholder. AppImageHub is the real provider addition.

Testing Strategy

Query Classification Coverage

Add direct-resolution tests for:

  • AppImageHub URL classification
  • appimagehub/<id> classification
  • malformed shorthand rejection

Adapter Coverage

Add AppImageHub adapter tests for:

  • successful normalization from URL and shorthand
  • successful resolution to an installable AppImage artifact
  • no-installable-artifact outcomes for valid provider items without usable assets

Search Coverage

Add search tests for:

  • AppImageHub provider hit mapping into SearchResult
  • installed-status annotation based on AppImageHub canonical IDs
  • cross-provider search continuing when one provider fails

Positional Query Coverage

Add app/CLI tests for:

  • positional query directly installing when a provider resolves installably
  • positional query falling back to search on unsupported query
  • positional query falling back to search on no-installable-artifact
  • positional query rendering empty search output when fallback search returns no matches

Registry/Smoke Coverage

Update adapter smoke and contract coverage to:

  • remove custom-json
  • include appimagehub

Delivery Notes

  • Keep resolve_query(...) strict; the fallback behavior belongs in a higher-level add/search decision.
  • Prefer small extensions to existing domain types over introducing a second search result model.
  • AppImageHub provider identity should be numeric-ID based from day one to avoid future migration churn.
  • Do not add title-derived aliases in this slice.