Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/BUNDLE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1266,10 +1266,13 @@ Bundles support multiple source formats for modules:
| Local path | `./modules/my-module` | Modules within the bundle |
| Relative path | `../shared-module` | Sibling directories |
| Git URL | `git+https://github.com/org/repo@main` | External modules |
| Git pinned to commit | `git+https://github.com/org/repo@32d4052dad46016f91ce698646580473e4121344` | Reproducible installs (eval runs, CI) |
| Git with subpath | `git+https://github.com/org/repo@main#subdirectory=modules/foo` | Module within larger repo |

**Local paths are resolved relative to the bundle's location.**

**Commit SHA pinning**: A ref that is a full 40-character hex commit SHA pins the source to that exact commit. Pinned sources are automatically skipped by `amplifier update` (a pinned ref can never have updates). Short/abbreviated SHAs are rejected by design — an ambiguous ref defeats reproducibility. Note that a typo'd but valid-looking SHA fails only after a full-history clone fallback: the fast single-commit fetch fails, git falls back to a full clone, and checkout then fails with a clear error (a failed pinned resolve can leave a cache entry behind, and a subsequent resolve of the same URI may serve it — clear the cache entry if a pin fails).

---

## Composition with includes:
Expand Down
9 changes: 9 additions & 0 deletions docs/URI_FORMATS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ Quick reference for source URIs. For complete details, see `parse_uri()` docstri
| **Local directory** | `/path/to/bundle/` (finds `bundle.md` inside) |
| **Git HTTPS** | `git+https://github.com/org/repo@main` |
| **Git SSH** | `git+ssh://git@github.com/org/repo@main` |
| **Git pinned to commit** | `git+https://github.com/org/repo@32d4052dad46016f91ce698646580473e4121344` |
| **Subdirectory** | `git+https://github.com/org/repo@main#subdirectory=path/to/bundle` |

### Pinning to a Commit SHA

A ref that is a full 40-character hex commit SHA pins the source to that exact commit. Use this for reproducible installs — evaluation runs, CI, anywhere "same URI, same code" must hold.

- Pinned sources are automatically **skipped by `amplifier update`** — a pinned ref can never have updates.
- Short/abbreviated SHAs are **rejected by design**: an ambiguous ref defeats reproducibility. Use the full 40 characters.
- A typo'd but valid-looking SHA is expensive: the fast single-commit fetch fails, git falls back to a **full-history clone**, and checkout then fails with a clear error (a failed pinned resolve can leave a cache entry behind, and a subsequent resolve of the same URI may serve it — clear the cache entry if a pin fails). A wrong SHA costs a full-history download before it errors.

## Common Examples

```python
Expand Down
45 changes: 45 additions & 0 deletions tests/test_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from amplifier_foundation.sources.git import GitSourceHandler
from amplifier_foundation.sources.git import _is_full_commit_sha
from amplifier_foundation.sources.http import HttpSourceHandler
from amplifier_foundation.sources.protocol import SourceStatus
from amplifier_foundation.sources.zip import ZipSourceHandler


Expand Down Expand Up @@ -375,6 +376,50 @@ def test_rejects_branch_names(self) -> None:
assert _is_full_commit_sha("v1.0.0") is False


class TestShaPredicatesAgree:
"""Invariant: the full-SHA rule is encoded twice and must not drift.

`_is_full_commit_sha` (regex, sources/git.py) and `SourceStatus.is_pinned`
(length + charset, sources/protocol.py) independently encode "40-char hex
commit SHA". A code comment claims they agree; this test checks it.

Note: `is_pinned` additionally treats v-prefixed version tags as pinned.
That branch is deliberate divergence outside the shared SHA rule, so
tag-shaped refs are excluded from the equality set and covered separately.
"""

@pytest.mark.parametrize(
"ref",
[
"32d4052dad46016f91ce698646580473e4121344", # 40-hex lowercase
"32D4052DAD46016F91CE698646580473E4121344", # 40-hex uppercase
"32D4052dad46016F91ce698646580473E4121344", # 40-hex mixed case
"3" * 39, # one char too short
"3" * 41, # one char too long
"g" + "3" * 39, # 40 chars, non-hex
"main", # branch name
"feat/some-branch", # branch name with slash
],
)
def test_predicates_agree_on_sha_classification(self, ref: str) -> None:
status = SourceStatus(
source_uri=f"git+https://example.com/org/repo@{ref}",
is_cached=True,
cached_ref=ref,
)
assert _is_full_commit_sha(ref) == status.is_pinned

def test_version_tag_is_pinned_but_not_a_sha(self) -> None:
"""Documents the intentional divergence: tags pin without being SHAs."""
status = SourceStatus(
source_uri="git+https://example.com/org/repo@v1.0.0",
is_cached=True,
cached_ref="v1.0.0",
)
assert _is_full_commit_sha("v1.0.0") is False
assert status.is_pinned is True


class TestGitSourceHandlerShaRefs:
"""Tests for resolving git refs pinned to a commit SHA."""

Expand Down
Loading