diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -122,19 +122,19 @@ permissions:
   contents: read
 
 jobs:
-  # Fails fast before any build: git dependencies must come from github.com/storytold, and the
+  # Fails fast before any build: git dependencies follow the repository's source policy, and the
   # lock file must not declare source replacement, patches or path overrides that could silently
   # swap one. Lives here (not in each app's release.yml) so every craftapp gets the gate at @main.
   # Every build job needs `version`, so these builds are gated transitively.
   git-sources:
-    name: Git dependencies come from storytold
+    name: Git dependencies match source policy
     runs-on: ubuntu-latest
     timeout-minutes: 10
     permissions:
       contents: read
     steps:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
-      - name: Git dependencies come from storytold
+      - name: Git dependencies match source policy
         run: |
           set -euo pipefail
           test -f Cargo.lock || { echo "Cargo.lock is missing"; exit 1; }
@@ -156,13 +156,22 @@ jobs:
           # Fails when Cargo.lock is out of date with the manifests.
           cargo metadata --locked --format-version 1 > /dev/null
           python3 - <<'PY'
-          import sys, tomllib
+          import os, sys, tomllib
           lock = tomllib.load(open("Cargo.lock", "rb"))
+          # One repository-scoped pilot pin. New callers, packages, versions or revisions
+          # require the supply-chain policy owner's review in this shared workflow.
+          edit_session = (
+              "edit-session", "0.1.0-alpha.2",
+              "git+https://github.com/PerchAnton/edit-session?rev=033f7c3a67b825b8f222288788cc61234c0c945d"
+              "#033f7c3a67b825b8f222288788cc61234c0c945d",
+          )
+          exceptions = {edit_session} if os.environ.get("GITHUB_REPOSITORY") == "storytold/pdfcraft" else set()
           bad = sorted({p["source"] for p in lock.get("package", [])
                         if p.get("source", "").startswith("git+")
-                        and not p["source"].startswith("git+https://github.com/storytold/")})
+                        and not p["source"].startswith("git+https://github.com/storytold/")
+                        and (p["name"], p["version"], p["source"]) not in exceptions})
           if bad:
-              print("Git dependencies outside github.com/storytold:", *bad, sep="\n")
+              print("Git dependencies outside the repository's source policy:", *bad, sep="\n")
               sys.exit(1)
           PY
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -20,7 +20,17 @@ Full multi-platform release pipeline for Craft apps:
 - **Linux RISC-V 64** (cross-compilation for riscv64gc-unknown-linux-gnu, producing deb, rpm, and tar.gz via SHA256-verified nfpm; built on Ubuntu 24.04, so it sets `GLIBC_FLOOR=2.39` for the app's `nfpm.yaml` to declare, see #44)
 - **Draft Release** (computes `SHA256SUMS.txt` and publishes/updates draft GitHub Release)
 
+The release gate rejects Git dependencies outside `github.com/storytold` by default.
+The single repository-scoped pilot exception is for the caller `storytold/pdfcraft`:
+`edit-session` version `0.1.0-alpha.2`, with both requested revision and resolved commit
+`033f7c3a67b825b8f222288788cc61234c0c945d` from `PerchAnton/edit-session`.
+It corresponds to the dependency proposed in [PdfCraft #4594](https://github.com/storytold/pdfcraft/pull/4594).
+Other apps and forks retain the strict default; callers cannot supply an exception list.
+New exceptions or pin changes require review in this shared workflow and the caller's CI.
+Source replacement, Cargo config patches/path overrides and unlocked metadata remain rejected.
+
 #### Caller Example:
+
 ```yaml
 name: Release
 
diff --git a/tests/test_git_sources.py b/tests/test_git_sources.py
new file mode 100644
--- /dev/null
+++ b/tests/test_git_sources.py
@@ -0,0 +1,120 @@
+"""Run the release source gate with synthetic lockfiles and no Cargo network access."""
+
+import json
+import os
+from pathlib import Path
+import subprocess
+import sys
+import tempfile
+import unittest
+
+import yaml
+
+
+ROOT = Path(__file__).resolve().parents[1]
+DATA = yaml.load((ROOT / '.github/workflows/release.yml').read_text(), Loader=yaml.BaseLoader)
+SCRIPT = next(step['run'] for step in DATA['jobs']['git-sources']['steps'] if 'run' in step)
+REVISION = '033f7c3a67b825b8f222288788cc61234c0c945d'
+EDIT_SESSION = {
+    'name': 'edit-session', 'version': '0.1.0-alpha.2',
+    'source': f'git+https://github.com/PerchAnton/edit-session?rev={REVISION}#{REVISION}',
+}
+
+
+class GitSourceTests(unittest.TestCase):
+    """Exercise the actual shell/Python gate, including its fail-closed boundaries."""
+
+    def run_gate(self, packages, repository='storytold/pdfcraft', configs=None,
+                 metadata_status=0, lockfile=True):
+        """Run the workflow step in isolation with a fake, argument-recording Cargo."""
+        with tempfile.TemporaryDirectory() as directory:
+            root = Path(directory)
+            if lockfile:
+                (root / 'Cargo.lock').write_text('version = 4\n' + ''.join(
+                    '\n[[package]]\n' + ''.join(f'{k} = {json.dumps(v)}\n' for k, v in p.items())
+                    for p in packages))
+            for name, source in (configs or {}).items():
+                path = root / name
+                path.parent.mkdir(parents=True, exist_ok=True)
+                path.write_text(source)
+            commands = root / 'commands'
+            commands.mkdir()
+            cargo = commands / 'cargo'
+            cargo.write_text(f'#!{sys.executable}\nimport json, sys\nfrom pathlib import Path\n'
+                             'Path("cargo-args.json").write_text(json.dumps(sys.argv[1:]))\n'
+                             f'sys.exit({metadata_status})\n')
+            cargo.chmod(0o755)
+            env = dict(os.environ, GITHUB_REPOSITORY=repository,
+                       PATH=str(commands) + os.pathsep + os.environ['PATH'])
+            result = subprocess.run(['bash', '-eu', '-c', SCRIPT], cwd=root, env=env,
+                                    text=True, capture_output=True, timeout=10)
+            arguments = root / 'cargo-args.json'
+            return result, json.loads(arguments.read_text()) if arguments.exists() else None
+
+    def test_pdfcraft_accepts_only_its_exact_edit_session_pin(self):
+        """The proposed PdfCraft dependency can pass without relaxing other sources."""
+        result, arguments = self.run_gate([EDIT_SESSION])
+        self.assertEqual(result.returncode, 0, result.stderr + result.stdout)
+        self.assertEqual(arguments, ['metadata', '--locked', '--format-version', '1'])
+
+    def test_exception_does_not_extend_to_other_repositories(self):
+        """Forks, other apps and absent caller identity keep the strict default."""
+        for repository in ('storytold/photocraft', 'PerchAnton/pdfcraft', '', 'other/pdfcraft'):
+            with self.subTest(repository=repository):
+                result, _ = self.run_gate([EDIT_SESSION], repository=repository)
+                self.assertNotEqual(result.returncode, 0)
+
+    def test_name_version_requested_revision_and_resolved_commit_must_all_match(self):
+        """Changing either revision representation or package identity revokes the exception."""
+        variants = [dict(EDIT_SESSION, name='another-package'),
+                    dict(EDIT_SESSION, version='0.1.0-alpha.3')]
+        for source in (
+            EDIT_SESSION['source'].replace('?rev=' + REVISION, '?rev=main'),
+            EDIT_SESSION['source'].replace('#' + REVISION, '#' + '0' * 40),
+            EDIT_SESSION['source'].replace('/edit-session?', '/another-repo?'),
+        ):
+            variants.append(dict(EDIT_SESSION, source=source))
+        for package in variants:
+            with self.subTest(package=package):
+                result, _ = self.run_gate([package])
+                self.assertNotEqual(result.returncode, 0)
+
+    def test_one_allowed_package_does_not_allow_a_second_external_source(self):
+        """An exact pilot pin cannot conceal another foreign Git dependency."""
+        foreign = dict(EDIT_SESSION, name='unreviewed', source='git+https://github.com/other/code#123')
+        result, _ = self.run_gate([EDIT_SESSION, foreign])
+        self.assertNotEqual(result.returncode, 0)
+
+    def test_existing_storytold_registry_and_local_packages_still_pass(self):
+        """The default source policy remains unchanged for every other caller."""
+        packages = [
+            {'name': 'local', 'version': '1.0.0'},
+            {'name': 'registry', 'version': '1.0.0', 'source': 'registry+https://github.com/rust-lang/crates.io-index'},
+            {'name': 'craft-ui', 'version': '1.0.0', 'source': 'git+https://github.com/storytold/craft-libs#123'},
+        ]
+        for repository in ('storytold/pdfcraft', 'storytold/photocraft', ''):
+            with self.subTest(repository=repository):
+                result, _ = self.run_gate(packages, repository=repository)
+                self.assertEqual(result.returncode, 0, result.stderr + result.stdout)
+
+    def test_source_replacement_patch_and_path_overrides_are_still_refused(self):
+        """Both Cargo config spellings reject substitutions before invoking Cargo."""
+        for filename in ('.cargo/config', '.cargo/config.toml'):
+            for config in ('[source.crates-io]\nreplace-with="other"\n',
+                           '[patch.crates-io]\n', 'paths=["../other"]\n'):
+                with self.subTest(filename=filename, config=config):
+                    result, arguments = self.run_gate([EDIT_SESSION], configs={filename: config})
+                    self.assertNotEqual(result.returncode, 0)
+                    self.assertIsNone(arguments)
+
+    def test_missing_lockfile_and_metadata_failure_still_fail(self):
+        """The exception cannot bypass lockfile presence or Cargo's locked resolution."""
+        result, arguments = self.run_gate([], lockfile=False)
+        self.assertNotEqual(result.returncode, 0)
+        self.assertIsNone(arguments)
+        result, _ = self.run_gate([EDIT_SESSION], metadata_status=23)
+        self.assertEqual(result.returncode, 23)
+
+
+if __name__ == '__main__':
+    unittest.main()
