feat: expand source provider resolution
This commit is contained in:
parent
9d8ec1e4fd
commit
eaa9a3b52d
23 changed files with 2582 additions and 34 deletions
|
|
@ -0,0 +1,227 @@
|
|||
# Source And Provider Expansion Design
|
||||
|
||||
## Goal
|
||||
|
||||
Expand install-source coverage beyond the current GitHub-centric path without collapsing providers, exact artifact sources, and update metadata into one abstraction.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The current codebase has a real mismatch between what the domain and adapter layers suggest is possible and what the end-to-end install pipeline actually treats as first-class:
|
||||
|
||||
- the domain source model already includes GitHub, GitLab, direct URL, and file
|
||||
- the adapter layer also advertises SourceForge and zsync shapes
|
||||
- the public source pipeline and most end-to-end behavior are still effectively GitHub-shaped
|
||||
|
||||
That creates two problems:
|
||||
|
||||
- adding new install origins risks becoming a copy of the GitHub path with provider-specific exceptions bolted on later
|
||||
- `zsync` is at risk of being modeled as a provider even though the current code treats it primarily as update metadata
|
||||
|
||||
## Design Goals
|
||||
|
||||
- make `GitLab` a real repository-backed install source
|
||||
- make `SourceForge` a real repository-backed install source
|
||||
- preserve `direct-url` as a first-class exact-resolution source
|
||||
- keep install origin semantics truthful in the registry
|
||||
- allow provider-native update re-resolution where it fits naturally
|
||||
- keep `zsync` as update metadata rather than forcing it into the install-source model
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- generic search UX across all providers
|
||||
- full behavioral parity across every provider in the first slice
|
||||
- rewriting the CLI presentation layer
|
||||
- promoting `zsync` into a first-class install source
|
||||
- inventing a universal provider abstraction that erases real capability differences
|
||||
|
||||
## Architectural Decision
|
||||
|
||||
Separate three concerns explicitly:
|
||||
|
||||
- `source kind`: how the user identified the install origin
|
||||
- `resolution strategy`: how the system turns that origin into a concrete installable artifact
|
||||
- `update channel`: how an installed application later discovers newer payloads
|
||||
|
||||
Under this model:
|
||||
|
||||
- `GitHub`, `GitLab`, and `SourceForge` are repository-backed source kinds
|
||||
- `direct-url` is an exact artifact source kind
|
||||
- `file` remains a local artifact source kind
|
||||
- `zsync` remains an update-channel and metadata mechanism
|
||||
|
||||
This keeps install and update semantics aligned without pretending every source type has provider-like behavior.
|
||||
|
||||
## Source Taxonomy
|
||||
|
||||
The input classification layer should classify user queries into a small, stable taxonomy:
|
||||
|
||||
- repository-backed sources
|
||||
- GitHub
|
||||
- GitLab
|
||||
- SourceForge
|
||||
- exact artifact sources
|
||||
- direct URL
|
||||
- local artifact sources
|
||||
- file
|
||||
|
||||
Classification should answer only:
|
||||
|
||||
- what kind of source the user provided
|
||||
- what canonical locator can be derived
|
||||
- whether release or asset hints are present
|
||||
- whether the origin is inherently trackable
|
||||
|
||||
Classification should not try to encode provider-specific release discovery beyond those normalized hints.
|
||||
|
||||
## Resolution Model
|
||||
|
||||
After classification, a resolver layer should convert a `SourceRef` into an installable release candidate.
|
||||
|
||||
### Repository-backed sources
|
||||
|
||||
Repository-backed resolution should:
|
||||
|
||||
- accept a canonical repository or project locator
|
||||
- discover release or download candidates using provider-specific logic
|
||||
- select a concrete AppImage payload when confidence is sufficient
|
||||
- return explicit structured failures when the repository exists but no installable AppImage is available
|
||||
|
||||
This applies to:
|
||||
|
||||
- GitHub
|
||||
- GitLab
|
||||
- SourceForge
|
||||
|
||||
### Exact artifact sources
|
||||
|
||||
Exact-resolution sources should:
|
||||
|
||||
- treat the user-provided locator as the concrete payload origin
|
||||
- derive best-effort metadata without pretending release discovery exists
|
||||
- remain installable even when rich update tracking is unavailable
|
||||
|
||||
This applies to:
|
||||
|
||||
- direct URL
|
||||
- file
|
||||
|
||||
## Registry Semantics
|
||||
|
||||
Registry persistence should preserve the truth about where the install came from.
|
||||
|
||||
Each installed record should continue to store:
|
||||
|
||||
- original source kind
|
||||
- original source locator
|
||||
- canonical locator when one exists
|
||||
- installed version
|
||||
- installed file metadata
|
||||
|
||||
The key rule is:
|
||||
|
||||
- install origin remains the origin the user chose
|
||||
- update mechanisms are additive metadata, not a replacement source identity
|
||||
|
||||
That means a direct URL install remains a direct URL install even if metadata later yields a richer update channel. A GitLab install remains a GitLab install even if update planning later uses provider-specific release rediscovery.
|
||||
|
||||
## Update Strategy
|
||||
|
||||
Update planning should use the install origin as the primary re-entry point.
|
||||
|
||||
For repository-backed sources:
|
||||
|
||||
- prefer provider-native re-resolution using the canonical locator
|
||||
- attach update channels discovered during metadata inspection as additional evidence
|
||||
|
||||
For exact-resolution sources:
|
||||
|
||||
- keep update support weak by default unless post-install metadata offers something stronger
|
||||
|
||||
For `zsync`:
|
||||
|
||||
- keep it as discovered metadata and update-channel input
|
||||
- do not rewrite the install source as `zsync`
|
||||
- do not require `zsync` install-source tests in this phase
|
||||
|
||||
This preserves a clean distinction between install origin and update mechanism.
|
||||
|
||||
## Error Handling
|
||||
|
||||
The design should distinguish unsupported semantics from runtime failure.
|
||||
|
||||
### Unsupported source semantics
|
||||
|
||||
Examples:
|
||||
|
||||
- malformed provider URL shapes
|
||||
- a project URL form we do not support yet
|
||||
- a provider source kind sent to the wrong resolver
|
||||
|
||||
These should fail early during classification or resolver selection with provider-aware messages.
|
||||
|
||||
### Resolvable source, but no installable artifact
|
||||
|
||||
Examples:
|
||||
|
||||
- repository exists but has no AppImage asset
|
||||
- release metadata is present but incomplete
|
||||
- multiple assets exist but none match install heuristics confidently
|
||||
|
||||
These should be structured resolution failures rather than generic unsupported errors.
|
||||
|
||||
### Transport or integration failure
|
||||
|
||||
Examples:
|
||||
|
||||
- HTTP download failures
|
||||
- metadata fetch failures
|
||||
- local staging or desktop integration failures
|
||||
|
||||
These remain operational failures in the existing install and update flow.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Testing should expand along capability lines rather than provider-specific copy-paste.
|
||||
|
||||
### Classification tests
|
||||
|
||||
Add coverage for:
|
||||
|
||||
- GitLab source forms
|
||||
- SourceForge source forms
|
||||
- direct URL edge cases
|
||||
- unsupported or malformed provider inputs
|
||||
|
||||
### Resolver contract tests
|
||||
|
||||
Each resolver should satisfy a shared contract:
|
||||
|
||||
- accepts valid source refs for its own kind
|
||||
- rejects source refs for other kinds
|
||||
- returns a concrete install candidate or a structured no-artifact result
|
||||
|
||||
### End-to-end flow tests
|
||||
|
||||
Add focused flow coverage for:
|
||||
|
||||
- install from GitLab source
|
||||
- install from direct URL
|
||||
- install from SourceForge source
|
||||
- truthful registry origin persistence
|
||||
- update planning that uses install origin plus additive metadata
|
||||
|
||||
### Non-goal tests
|
||||
|
||||
Do not force `zsync` into install-source tests for this phase.
|
||||
|
||||
## Rollout
|
||||
|
||||
Recommended rollout order:
|
||||
|
||||
1. normalize the source taxonomy and resolver interfaces
|
||||
2. wire GitLab and direct URL cleanly through install and registry persistence
|
||||
3. add SourceForge using the same resolver contract, limited to supported URL and project forms
|
||||
4. extend update planning only where the source kind supports provider-native re-resolution naturally
|
||||
5. leave `zsync` unchanged except to ensure it remains additive update metadata
|
||||
|
||||
This keeps the product honest: each added source gets explicit semantics instead of being forced through a renamed GitHub pathway.
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
# Source And Provider Expansion Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Make GitLab and SourceForge real repository-backed install sources, preserve direct URL as a first-class exact-resolution source, and keep zsync as update metadata rather than an install provider.
|
||||
|
||||
**Architecture:** Normalize the source taxonomy first, then add a capability-shaped resolver layer that distinguishes repository-backed sources from exact artifact sources. Preserve truthful install origin data in the registry and let update planning attach richer metadata without rewriting source identity.
|
||||
|
||||
**Tech Stack:** Rust, Cargo workspace, `aim-core` source and adapter modules, existing fixture-backed integration tests in `crates/aim-core/tests`, CLI end-to-end tests in `crates/aim-cli/tests`, existing registry and update planning code.
|
||||
|
||||
## Follow-up Status
|
||||
|
||||
Task 1 hit a classification ambiguity blocker after the initial rollout. The follow-up design and execution live in `.plans/007-source-provider-expansion/2026-03-20-task-1-ambiguity-handoff-addendum.md`.
|
||||
|
||||
Current state on this branch:
|
||||
|
||||
- ambiguous GitLab deep paths and one SourceForge nested download path are now preserved as provider-owned candidate kinds during classification
|
||||
- the first GitLab candidate slice now resolves as a concrete repository-backed install source at the adapter layer
|
||||
- the SourceForge `files/releases/stable/download` candidate slice now resolves as a concrete latest-download install source at the adapter layer
|
||||
- the SourceForge `files/releases/v*/download` slice is now preserved as a provider-owned candidate and reports `NoInstallableArtifact`
|
||||
- unsupported queries remain distinct from provider-owned no-artifact outcomes
|
||||
|
||||
Classifier policy for follow-up work:
|
||||
|
||||
- accept explicit concrete shapes
|
||||
- accept explicit provider-candidate shapes
|
||||
- reject everything else
|
||||
|
||||
Future changes should expand the allowlist deliberately rather than adding broad negative-rule coverage for every unsupported provider page family.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Lock down source taxonomy with failing classification tests
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/tests/query_resolution.rs`
|
||||
- Modify: `crates/aim-core/src/source/input.rs`
|
||||
- Modify: `crates/aim-core/src/domain/source.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add classification tests that cover:
|
||||
- GitLab repository and release-like URL forms that should classify as `GitLab`
|
||||
- supported SourceForge URL forms that should classify as `SourceForge`
|
||||
- direct URLs that must remain `DirectUrl`
|
||||
- malformed provider URLs that must fail as unsupported
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test query_resolution`
|
||||
Expected: FAIL because SourceForge is not yet part of the public source taxonomy and current classification rules are too narrow.
|
||||
|
||||
**Step 3: Write minimal classification changes**
|
||||
|
||||
Update the source domain and classifier so the public source taxonomy includes the approved source kinds and supported input forms without introducing zsync as an install source.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test query_resolution`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/tests/query_resolution.rs crates/aim-core/src/source/input.rs crates/aim-core/src/domain/source.rs
|
||||
git commit -m "test: cover expanded source taxonomy"
|
||||
```
|
||||
|
||||
### Task 2: Add a shared resolver contract for source capabilities
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/src/adapters/traits.rs`
|
||||
- Modify: `crates/aim-core/src/adapters/mod.rs`
|
||||
- Modify: `crates/aim-core/tests/adapter_contract.rs`
|
||||
- Modify: `crates/aim-core/src/app/query.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add contract tests that assert:
|
||||
- repository-backed resolvers accept only their own source kinds
|
||||
- exact-resolution resolvers accept only exact artifact kinds
|
||||
- resolvers can return structured “no installable artifact” outcomes rather than collapsing to unsupported
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test adapter_contract`
|
||||
Expected: FAIL because the current adapter trait does not distinguish source capability outcomes cleanly enough.
|
||||
|
||||
**Step 3: Write minimal resolver contract changes**
|
||||
|
||||
Refine the shared adapter or resolver contract to represent:
|
||||
- unsupported source kind
|
||||
- supported source with successful artifact resolution
|
||||
- supported source with no installable artifact found
|
||||
|
||||
Keep the API small and do not add terminal concerns.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test adapter_contract`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/src/adapters/traits.rs crates/aim-core/src/adapters/mod.rs crates/aim-core/tests/adapter_contract.rs crates/aim-core/src/app/query.rs
|
||||
git commit -m "feat: add capability-shaped resolver contract"
|
||||
```
|
||||
|
||||
### Task 3: Make GitLab a real repository-backed install source
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/src/adapters/gitlab.rs`
|
||||
- Modify: `crates/aim-core/src/app/add.rs`
|
||||
- Modify: `crates/aim-core/tests/adapter_contract.rs`
|
||||
- Modify: `crates/aim-core/tests/install_integration.rs`
|
||||
- Modify: `crates/aim-cli/tests/end_to_end_cli.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that assert:
|
||||
- a GitLab source resolves to a concrete install candidate
|
||||
- install flow persists a truthful GitLab install origin
|
||||
- CLI integration can install a fixture-backed GitLab source end to end
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test install_integration`
|
||||
Expected: FAIL because GitLab resolution is currently placeholder-level and not wired into the add flow meaningfully.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Implement GitLab-specific repository-backed resolution using the new resolver contract and thread the result through the add flow without changing direct URL or zsync semantics.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test install_integration`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/src/adapters/gitlab.rs crates/aim-core/src/app/add.rs crates/aim-core/tests/adapter_contract.rs crates/aim-core/tests/install_integration.rs crates/aim-cli/tests/end_to_end_cli.rs
|
||||
git commit -m "feat: add gitlab install source resolution"
|
||||
```
|
||||
|
||||
### Task 4: Preserve direct URL as an exact-resolution source
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/src/adapters/direct_url.rs`
|
||||
- Modify: `crates/aim-core/src/app/add.rs`
|
||||
- Modify: `crates/aim-core/tests/install_integration.rs`
|
||||
- Modify: `crates/aim-cli/tests/end_to_end_cli.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that assert:
|
||||
- direct URL installs continue to resolve exactly to the provided artifact
|
||||
- registry persistence keeps the original direct URL source kind and locator
|
||||
- no provider-like reclassification occurs after install
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test install_integration`
|
||||
Expected: FAIL if the new resolver contract or registry changes accidentally regress exact-resolution behavior.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Adjust the direct URL path to use the new resolver interfaces while preserving exact-resolution semantics and best-effort metadata only.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test install_integration`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/src/adapters/direct_url.rs crates/aim-core/src/app/add.rs crates/aim-core/tests/install_integration.rs crates/aim-cli/tests/end_to_end_cli.rs
|
||||
git commit -m "feat: preserve direct url exact resolution semantics"
|
||||
```
|
||||
|
||||
### Task 5: Add SourceForge as a repository-backed source for supported project forms
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/src/adapters/sourceforge.rs`
|
||||
- Modify: `crates/aim-core/src/source/input.rs`
|
||||
- Modify: `crates/aim-core/src/app/add.rs`
|
||||
- Modify: `crates/aim-core/tests/adapter_contract.rs`
|
||||
- Modify: `crates/aim-core/tests/install_integration.rs`
|
||||
- Modify: `crates/aim-cli/tests/end_to_end_cli.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that assert:
|
||||
- supported SourceForge URL or project forms classify correctly
|
||||
- SourceForge resolution can produce a concrete install candidate
|
||||
- SourceForge installs persist truthful origin data
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test adapter_contract`
|
||||
Expected: FAIL because SourceForge currently returns unsupported from its adapter.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Implement only the supported SourceForge project or download forms needed for exact current-product scope. Return structured no-artifact failures for valid-but-non-installable projects.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test adapter_contract`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/src/adapters/sourceforge.rs crates/aim-core/src/source/input.rs crates/aim-core/src/app/add.rs crates/aim-core/tests/adapter_contract.rs crates/aim-core/tests/install_integration.rs crates/aim-cli/tests/end_to_end_cli.rs
|
||||
git commit -m "feat: add sourceforge install source resolution"
|
||||
```
|
||||
|
||||
### Task 6: Keep registry origin truthful and update metadata additive
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/src/registry/model.rs`
|
||||
- Modify: `crates/aim-core/src/app/add.rs`
|
||||
- Modify: `crates/aim-core/src/app/update.rs`
|
||||
- Modify: `crates/aim-core/src/update/channels.rs`
|
||||
- Modify: `crates/aim-core/tests/update_planning.rs`
|
||||
- Modify: `crates/aim-core/tests/registry_roundtrip.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that assert:
|
||||
- GitLab and SourceForge installs preserve original source kind and locator after roundtrip persistence
|
||||
- direct URL installs remain direct URL installs after metadata inspection
|
||||
- discovered update channels augment stored state without rewriting source identity
|
||||
- zsync remains update metadata only
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test update_planning`
|
||||
Expected: FAIL because update planning and registry expectations do not yet fully encode the approved source-versus-update split.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Adjust registry and update planning logic so install origin remains canonical and update channels remain additive metadata.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test update_planning`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/src/registry/model.rs crates/aim-core/src/app/add.rs crates/aim-core/src/app/update.rs crates/aim-core/src/update/channels.rs crates/aim-core/tests/update_planning.rs crates/aim-core/tests/registry_roundtrip.rs
|
||||
git commit -m "feat: preserve source identity through update planning"
|
||||
```
|
||||
|
||||
### Task 7: Improve provider-aware error reporting without changing CLI shape
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aim-core/src/adapters/traits.rs`
|
||||
- Modify: `crates/aim-core/src/app/add.rs`
|
||||
- Modify: `crates/aim-cli/src/lib.rs`
|
||||
- Modify: `crates/aim-core/tests/install_failures.rs`
|
||||
- Modify: `crates/aim-cli/tests/end_to_end_cli.rs`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that distinguish:
|
||||
- unsupported source semantics
|
||||
- supported source with no installable artifact
|
||||
- transport or integration failure
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cargo test --package aim-core --test install_failures`
|
||||
Expected: FAIL because failure reasons are not yet structured enough to preserve those distinctions.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Introduce explicit failure categories and thread them through the add flow so the CLI can render clearer provider-aware messages without changing the progress UI architecture.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test --package aim-core --test install_failures`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add crates/aim-core/src/adapters/traits.rs crates/aim-core/src/app/add.rs crates/aim-cli/src/lib.rs crates/aim-core/tests/install_failures.rs crates/aim-cli/tests/end_to_end_cli.rs
|
||||
git commit -m "feat: clarify provider-aware source resolution failures"
|
||||
```
|
||||
|
||||
### Task 8: Full verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md`
|
||||
- Modify: `crates/aim-core/tests/github_source_discovery.rs`
|
||||
- Modify: `crates/aim-core/tests/query_resolution.rs`
|
||||
- Modify: `crates/aim-core/tests/install_integration.rs`
|
||||
- Modify: `crates/aim-core/tests/update_planning.rs`
|
||||
- Modify: `crates/aim-cli/tests/end_to_end_cli.rs`
|
||||
|
||||
**Step 1: Tighten any stale expectations**
|
||||
|
||||
Update docs and tests so the product contract matches the approved design:
|
||||
- GitLab and SourceForge are install sources
|
||||
- direct URL remains exact-resolution
|
||||
- zsync remains update metadata
|
||||
|
||||
**Step 2: Run focused workspace verification**
|
||||
|
||||
Run: `cargo test --package aim-core --test query_resolution --test adapter_contract --test install_integration --test update_planning --test install_failures`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 3: Run CLI verification**
|
||||
|
||||
Run: `cargo test --package aim-cli --test end_to_end_cli`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 4: Run full workspace verification**
|
||||
|
||||
Run: `cargo fmt --all && cargo test --workspace`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add README.md crates/aim-core/tests/github_source_discovery.rs crates/aim-core/tests/query_resolution.rs crates/aim-core/tests/install_integration.rs crates/aim-core/tests/update_planning.rs crates/aim-core/tests/install_failures.rs crates/aim-cli/tests/end_to_end_cli.rs
|
||||
git commit -m "docs: align source provider contract and tests"
|
||||
```
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
# Task 1 Ambiguity Handoff Addendum
|
||||
|
||||
## Goal
|
||||
|
||||
Resolve the Task 1 blocker by moving ambiguous GitLab and SourceForge URL handling out of pure taxonomy heuristics and into provider-aware resolution.
|
||||
|
||||
## Problem Restatement
|
||||
|
||||
The blocker is not that the classifier is missing a few more path rules.
|
||||
|
||||
The blocker is that some provider-hosted URL shapes do not carry enough information to determine final install semantics from path shape alone.
|
||||
|
||||
Two cases are responsible for the review churn:
|
||||
|
||||
- GitLab deep paths where a segment may be either a subgroup slug or a resource-like segment
|
||||
- SourceForge `files/.../download` paths where the same suffix can represent either a concrete file download or a folder-style endpoint
|
||||
|
||||
Trying to settle those cases in `resolve_query(...)` forces the code into a false choice:
|
||||
|
||||
- accept ambiguous inputs too early and misclassify them
|
||||
- reject provider-owned inputs too early and lose useful context
|
||||
|
||||
## Design Decision
|
||||
|
||||
Adopt an ambiguity handoff model.
|
||||
|
||||
That means:
|
||||
|
||||
- the classifier remains authoritative only for cases it can determine with high confidence
|
||||
- ambiguous provider-hosted inputs are preserved as provider-owned candidates rather than flattened into `Unsupported`
|
||||
- provider adapters become the layer that decides whether an ambiguous input is:
|
||||
- a supported repository or project source
|
||||
- a supported exact download form
|
||||
- a supported source with no installable artifact
|
||||
- truly unsupported for that provider
|
||||
|
||||
## Contract Boundary
|
||||
|
||||
### Classification policy
|
||||
|
||||
The classifier should use a strict positive-matching contract.
|
||||
|
||||
Each input shape must land in exactly one of three buckets:
|
||||
|
||||
- accept as a definite supported source
|
||||
- accept as an explicit provider-owned candidate
|
||||
- reject as unsupported
|
||||
|
||||
This means the classifier should prefer a small allowlist of accepted shapes over an expanding catalog of bespoke rejection rules.
|
||||
|
||||
Negative rules are still allowed when needed to protect a known false-positive family, but they are defensive exceptions, not the main design strategy.
|
||||
|
||||
### Classification must do
|
||||
|
||||
- identify definite GitHub, GitLab, SourceForge, direct URL, and file inputs
|
||||
- accept only explicitly enumerated concrete shapes or explicitly enumerated candidate shapes
|
||||
- preserve canonical locator hints when they are certain
|
||||
- preserve enough raw path context for later provider-specific disambiguation
|
||||
- continue classifying concrete artifact URLs as `DirectUrl` when the classifier can say so confidently
|
||||
|
||||
### Classification must not do
|
||||
|
||||
- grow by accumulating one-off rejection rules for every unsupported provider page family
|
||||
- guess whether a GitLab deep path is a subgroup path or a resource page when the path shape is ambiguous
|
||||
- guess whether a SourceForge nested `files/.../download` path is a file or folder endpoint when the path shape is ambiguous
|
||||
- perform provider-specific network discovery
|
||||
|
||||
### Resolver layer must do
|
||||
|
||||
- own final interpretation of ambiguous provider-hosted inputs
|
||||
- return structured outcomes through the adapter contract
|
||||
- keep `UnsupportedSource` reserved for sources the adapter genuinely does not own
|
||||
- use `NoInstallableArtifact` for provider-owned inputs that are valid but not installable under current scope
|
||||
|
||||
## Proposed Source Model Adjustment
|
||||
|
||||
Introduce an explicit handoff shape for ambiguous provider-owned inputs.
|
||||
|
||||
The minimal acceptable form is:
|
||||
|
||||
- preserve the original locator
|
||||
- preserve provider ownership
|
||||
- preserve any canonical parts that are certain
|
||||
- add a signal that provider resolution is still required before install semantics are known
|
||||
|
||||
This can be modeled either as:
|
||||
|
||||
1. a dedicated ambiguity marker on `SourceRef`
|
||||
2. additional normalized kinds representing provider-owned unresolved candidates
|
||||
|
||||
The preferred direction is additional normalized kinds, because they keep the ambiguity visible in tests and logs without adding a free-form boolean that can drift.
|
||||
|
||||
Illustrative shapes:
|
||||
|
||||
- `NormalizedSourceKind::GitLabCandidate`
|
||||
- `NormalizedSourceKind::SourceForgeCandidate`
|
||||
|
||||
The exact enum names are secondary. The important part is making unresolved provider ownership explicit.
|
||||
|
||||
## Provider Responsibilities
|
||||
|
||||
### GitLab
|
||||
|
||||
GitLab adapter logic should decide whether a GitLab-owned ambiguous input is:
|
||||
|
||||
- a valid repository locator
|
||||
- a release-like source with concrete version semantics
|
||||
- a provider-owned but non-installable resource page
|
||||
- unsupported because it does not fit the adapter's supported contract
|
||||
|
||||
Initial scope should stay narrow:
|
||||
|
||||
- keep current definite repository and release-like support
|
||||
- add only one or two ambiguous deep-path cases as a first expansion slice
|
||||
- do not try to solve every GitLab resource URL family at once
|
||||
|
||||
### SourceForge
|
||||
|
||||
SourceForge adapter logic should decide whether a SourceForge-owned ambiguous input is:
|
||||
|
||||
- a concrete latest-download install source
|
||||
- a concrete direct artifact URL
|
||||
- a provider-owned project or folder view with no installable artifact
|
||||
- unsupported for current source scope
|
||||
|
||||
Initial scope should stay narrow:
|
||||
|
||||
- keep bare project URLs as provider-owned and non-installable
|
||||
- keep `files/latest/download` as the first concrete repository-backed install source
|
||||
- add exactly one nested `files/.../download` ambiguity case to the adapter decision path
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
The blocker should be resolved by shifting assertions to the right layer.
|
||||
|
||||
### Classification tests
|
||||
|
||||
Update `query_resolution` coverage so ambiguous cases assert provider ownership and handoff state instead of asserting final install semantics.
|
||||
|
||||
Coverage should be organized around accepted-shape allowlists:
|
||||
|
||||
- accepted concrete shapes
|
||||
- accepted candidate shapes
|
||||
- a small number of representative false-positive guards
|
||||
|
||||
Examples:
|
||||
|
||||
- a concrete SourceForge artifact download still classifies as `DirectUrl`
|
||||
- a definite GitLab repository form still classifies as `GitLab`
|
||||
- an ambiguous GitLab deep path becomes a GitLab-owned candidate, not `Unsupported`
|
||||
- an ambiguous SourceForge nested download path becomes a SourceForge-owned candidate, not prematurely direct or unsupported
|
||||
|
||||
### Adapter contract tests
|
||||
|
||||
Add tests that assert adapters make the final decision for ambiguous handoff inputs.
|
||||
|
||||
Examples:
|
||||
|
||||
- GitLab candidate path resolves to supported repository semantics
|
||||
- GitLab candidate path resolves to `NoInstallableArtifact`
|
||||
- SourceForge candidate path resolves to `Resolved`
|
||||
- SourceForge candidate path resolves to `NoInstallableArtifact`
|
||||
|
||||
### Install and failure tests
|
||||
|
||||
Keep install-flow tests focused on supported concrete outcomes.
|
||||
|
||||
Keep failure tests focused on the distinction between:
|
||||
|
||||
- unsupported query
|
||||
- provider-owned source with no installable artifact
|
||||
- runtime install or transport failure
|
||||
|
||||
## Incremental Execution Plan
|
||||
|
||||
### Phase 1: Lock the boundary
|
||||
|
||||
- update the design docs to state that classification only decides what it can know with certainty
|
||||
- record that ambiguous provider-hosted inputs are a resolver concern
|
||||
|
||||
### Phase 2: Add handoff representation
|
||||
|
||||
- extend the source model with explicit provider-candidate semantics
|
||||
- thread that representation through the query classifier
|
||||
|
||||
### Phase 3: Shift one GitLab ambiguity case
|
||||
|
||||
- add a failing classification test for an ambiguous GitLab deep path
|
||||
- classify it as a GitLab-owned candidate
|
||||
- add adapter contract coverage for the GitLab decision
|
||||
|
||||
### Phase 4: Shift one SourceForge ambiguity case
|
||||
|
||||
- add a failing classification test for a nested `files/.../download` ambiguity case
|
||||
- classify it as a SourceForge-owned candidate
|
||||
- add adapter contract coverage for the SourceForge decision
|
||||
|
||||
### Phase 5: Tighten error reporting
|
||||
|
||||
- make sure ambiguous provider-owned inputs that do not yield installable artifacts surface as `NoInstallableArtifact`
|
||||
- avoid regressing them into unsupported-query failures
|
||||
|
||||
## Progress Update
|
||||
|
||||
Current implementation status in this branch:
|
||||
|
||||
- Phase 1 is complete. The classifier-versus-adapter boundary is now documented explicitly in this addendum.
|
||||
- Phase 2 is complete. `GitLabCandidate` and `SourceForgeCandidate` now exist in the source model and are produced by classification for the narrow ambiguity cases under test.
|
||||
- Phase 3 is complete for the first GitLab slice. `https://gitlab.com/<group>/<subgroup>/releases/<repo>` remains a classified candidate, but the GitLab adapter now resolves it as repository semantics with a derived canonical locator.
|
||||
- Phase 4 is complete for two SourceForge slices. `https://sourceforge.net/projects/<project>/files/releases/stable/download` remains a classified candidate and now resolves as a provider-owned latest-download source. `https://sourceforge.net/projects/<project>/files/releases/v*/download` is now preserved as a provider-owned candidate and surfaces as `NoInstallableArtifact`.
|
||||
- Phase 5 is partially complete. Provider-owned ambiguous inputs now distinguish unsupported-query failures from no-artifact outcomes, and both GitLab and SourceForge have at least one adapter-owned positive resolution path.
|
||||
|
||||
The current intended classifier contract is:
|
||||
|
||||
- accept explicit supported shapes
|
||||
- accept explicit candidate shapes
|
||||
- reject everything else
|
||||
|
||||
That contract is intentionally stricter than heuristic best-effort classification and intentionally narrower than provider resolution.
|
||||
|
||||
What remains intentionally out of scope for this slice:
|
||||
|
||||
- additional GitLab candidate families beyond the first repository-style deep path
|
||||
- broader SourceForge folder and version-path families beyond the `releases/stable/download` and narrow `releases/v*/download` rules
|
||||
- any network-backed provider discovery in classification
|
||||
|
||||
## Success Criteria
|
||||
|
||||
This blocker is considered resolved when:
|
||||
|
||||
- `query_resolution` no longer oscillates over ambiguous provider-owned shapes
|
||||
- ambiguous provider-hosted URLs are no longer forced into final install semantics during classification
|
||||
- adapters are the only place where ambiguous provider paths are interpreted fully
|
||||
- failure reporting distinguishes unsupported inputs from provider-owned non-installable inputs
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- solving every ambiguous GitLab deep-path variant in one pass
|
||||
- solving every SourceForge nested folder or version path in one pass
|
||||
- introducing network discovery into the pure query classifier
|
||||
- expanding current supported source scope beyond what the adapter tests can defend clearly
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# Task 1 Blocker Note
|
||||
|
||||
## Summary
|
||||
|
||||
Task 1 is blocked on an unresolved contract decision, not on a failing implementation.
|
||||
|
||||
The focused taxonomy test suite currently passes:
|
||||
|
||||
- `cargo test --package aim-core --test query_resolution`
|
||||
|
||||
But code review keeps surfacing the same underlying issue: some GitLab and SourceForge URL shapes are ambiguous when classified from URL structure alone.
|
||||
|
||||
## Current Blocker
|
||||
|
||||
Two classes of URLs cannot be classified with high confidence using only path-shape heuristics:
|
||||
|
||||
1. GitLab deep paths
|
||||
- Example ambiguity: a path segment may be either a subgroup slug or a resource-like segment.
|
||||
- This makes URLs such as deeply nested subgroup paths indistinguishable from some non-repository resource paths without provider-aware resolution.
|
||||
|
||||
2. SourceForge nested `files/.../download` paths
|
||||
- Some are concrete file downloads.
|
||||
- Some are folder-style or version-folder download endpoints.
|
||||
- URL shape alone does not reliably distinguish the two in every case.
|
||||
|
||||
## Why This Blocks Task 1
|
||||
|
||||
Task 1 is supposed to harden source taxonomy. It is not supposed to solve provider semantics end to end.
|
||||
|
||||
At this point, the remaining disagreement is about where ambiguity should be resolved:
|
||||
|
||||
- in classification, using increasingly complex heuristics
|
||||
- or later, in provider-aware resolution logic
|
||||
|
||||
Trying to solve this entirely in Task 1 has led to oscillation between:
|
||||
|
||||
- permissive rules that accept too many ambiguous URLs
|
||||
- conservative rules that reject URLs a reviewer considers potentially valid
|
||||
|
||||
## Recommended Resolution
|
||||
|
||||
Freeze Task 1 on the current conservative contract and move ambiguity handling into Task 2.
|
||||
|
||||
That keeps Task 1 scoped to explicit supported forms and lets the resolver contract decide how to treat ambiguous provider URLs with richer semantics.
|
||||
|
||||
## Current Practical State
|
||||
|
||||
- taxonomy tests are green
|
||||
- GitLab, SourceForge, and direct URL shapes are covered by focused tests
|
||||
- ambiguous provider URL handling remains the only unresolved review topic for Task 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue