diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 264c86006..62f66c382 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -343,7 +343,7 @@ jobs: run: | VERSION=${GITHUB_REF#refs/tags/v} WHL_VERSION=$(python3 -c "import re,sys; v='${VERSION}'; m=re.match(r'^(\d+\.\d+\.\d+)-(.+)$',v); print(m.group(1)+'+'+m.group(2).replace('-','.')if m else v)") - pip install "wheels/periscope-${WHL_VERSION}-py3-none-manylinux_2_28_x86_64.whl" + pip install "wheels/periscope_agentsview-${WHL_VERSION}-py3-none-manylinux_2_28_x86_64.whl" periscope --version - name: Publish to PyPI diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index f880b1bf6..de55fcc52 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -7,7 +7,7 @@ description: Matryoshka model, branch roles, and fork invariants for diazMelgare > **Status:** Living document. Update after every upstream replay and fork enhancement. > **Canonical integration branch:** `merged` -> **Last updated:** 2026-07-28 +> **Last updated:** 2026-07-30 **Operator policy:** [`docs/guides/periscope-upstream-sync-blueprint.md`](guides/periscope-upstream-sync-blueprint.md) **Rename checklist:** [`docs/guides/agentsview-to-periscope-rename-catalogue.md`](guides/agentsview-to-periscope-rename-catalogue.md) @@ -171,26 +171,72 @@ The shipping integration line uses ## Versioning and release tags -Semver pre-release identity: +### Fork release tags (Layer 3 — Periscope identity) + +Fork releases use a **grandmother-derived semver** plus a **`-periscope.N`** +suffix that marks the fork’s major AgentsView integrative merge generation on +`merged`. Product binaries and CI artifacts stay branded `periscope`; the +`agentsview` mirror branch name is unrelated to release tags. + +**Formula:** ``` -v0.(upstream_minor + 1).2-periscope.2 +v0.(upstream_minor + 1).2-periscope.N ``` +| Part | Meaning | +| --- | --- | +| `v0.(upstream_minor + 1)` | kenn-io/agentsview release minor + 1 (readable upstream base) | +| `.2` | Periscope patch slot on that upstream-derived minor (established convention) | +| `-periscope.N` | Fork identity generation — **N** = major AgentsView → `merged` integrative absorption count | + +**Grandmother → fork tag map (canonical examples):** + +| Grandmother (`kenn-io/agentsview`) | Fork release tag | Note | +| --- | --- | --- | +| v0.28.x (latentsignal era) | `v0.29.2-periscope.2` | First shipped fork generation | +| v0.39.0 (current grandmother) | **`v0.40.2-periscope.3`** | **Third** major AgentsView integrative merge onto `merged` | + +**Current target (post–third AgentsView merge):** `v0.40.2-periscope.3` — bump +`jetbrains-plugin/gradle.properties`, release `-ldflags`, and operator tags when +that integrative merge lands on `merged` (not when the mirror alone advances). + +Historical tags (`v0.29.2-periscope.2-*`, `v0.29.2-periscope.3-*`) remain valid +release pointers; do not rewrite them. + +### Desktop app semver (parallel track) + +`desktop/src-tauri/tauri.conf.json` `version` follows the **grandmother desktop +tree** (currently `0.12.1` on both mirror and `merged`). `productName` on +`merged` is **Periscope**; on the mirror it remains **AgentsView**. Bump desktop +semver when grandmother bumps it during replay — independent of the fork tag +suffix above. + +### Commit-suffixed release tags + Tags **always embed the short commit hash** of the release commit. Convention adopted in `5bd2e8a` (May 2026): ``` -v{semver}-{8-char-commit} e.g. v0.29.2-periscope.2-657a1090 +v{semver}-{8-char-commit} e.g. v0.40.2-periscope.3-657a1090 ``` ```bash COMMIT=$(git rev-parse --short=8 HEAD) -VERSION="v0.29.2-periscope.2" +VERSION="v0.40.2-periscope.3" git tag -a "${VERSION}-${COMMIT}" -m "Release ${VERSION}-${COMMIT}" # Push only after operator review — never automatic from tooling here. ``` +After each major AgentsView integrative merge onto `merged`: + +```bash +# Example: grandmother v0.39.0 → v0.40.2-periscope.3 (third absorption) +OLD_VERSION="v0.29.2-periscope.2" +NEW_VERSION="v0.40.2-periscope.3" +# Update jetbrains-plugin/gradle.properties and release ldflags; then tag as above. +``` + --- ## Desktop app identity diff --git a/docs/guides/periscope-modernization-status.md b/docs/guides/periscope-modernization-status.md index 3bf08eb3c..e11d2e720 100644 --- a/docs/guides/periscope-modernization-status.md +++ b/docs/guides/periscope-modernization-status.md @@ -1,6 +1,6 @@ # Periscope modernization status -**Updated:** 2026-07-28 +**Updated:** 2026-07-30 **Base commit:** `2b6e5128` (3way modernization replay) **Integration branch:** `merged` **Replaces:** historical `PROGRESS.md` completion ledger (archive only — not active truth) @@ -35,26 +35,33 @@ related specs. --- +## Fork release tags (Layer 3 — Periscope identity) + +See [`ARCHITECTURE.md`](../ARCHITECTURE.md) § Versioning for the full matryoshka +context. Summary: + +| Track | Rule | +| --- | --- | +| Fork release tag | `v0.(upstream_minor + 1).2-periscope.N` — **N** = major AgentsView → `merged` merge generation | +| Grandmother v0.39.0 | Target **`v0.40.2-periscope.3`** (third integrative absorption) | +| Desktop `tauri.conf.json` | Follows grandmother semver (`0.12.1` today); `productName` **Periscope** on `merged` | + +Historical shipped tag: `v0.29.2-periscope.2-*` (grandmother v0.28 era). + ## Release tag convention (5bd2e8a) Fork releases **always embed the short commit hash** of the release commit. Adopted in commit `5bd2e8a421c326a9a40e73437930ba76f475b9cb` (May 2026). ``` -v{semver}-{8-char-commit} e.g. v0.29.2-periscope.2-657a1090 -``` - -Semver pre-release identity on `merged`: - -``` -v0.(upstream_minor + 1).2-periscope.2 +v{semver}-{8-char-commit} e.g. v0.40.2-periscope.3-657a1090 ``` Operator tagging (after verification — never automatic from tooling): ```bash COMMIT=$(git rev-parse --short=8 HEAD) -VERSION="v0.29.2-periscope.2" +VERSION="v0.40.2-periscope.3" git tag -a "${VERSION}-${COMMIT}" -m "Release ${VERSION}-${COMMIT}" ``` @@ -117,7 +124,7 @@ shell environment overrides (see `desktop/src-tauri/src/lib.rs`). # Release tag (operator, after verification) COMMIT=$(git rev-parse --short=8 HEAD) -git tag -a "v0.29.2-periscope.2-${COMMIT}" -m "Release v0.29.2-periscope.2-${COMMIT}" +git tag -a "v0.40.2-periscope.3-${COMMIT}" -m "Release v0.40.2-periscope.3-${COMMIT}" ``` --- diff --git a/scripts/build_wheels.py b/scripts/build_wheels.py index 2e7c9edee..9e1b5c104 100644 --- a/scripts/build_wheels.py +++ b/scripts/build_wheels.py @@ -44,6 +44,18 @@ def normalize_wheel_version(version: str) -> str: return version +# --------------------------------------------------------------------------- +# Package identity +# --------------------------------------------------------------------------- + +# PyPI project name. The upstream name "periscope" is registered by an +# unrelated project (a subtitle downloader), so this fork publishes under +# a distinct distribution name. The installed console script and the +# internal module/import name stay "periscope" -- only the PyPI project +# identity (and therefore the wheel filename and dist-info dir) differ. +PYPI_DIST_NAME = "periscope-agentsview" +PYPI_DIST_NAME_NORMALIZED = PYPI_DIST_NAME.replace("-", "_") + # --------------------------------------------------------------------------- # Platform constants # --------------------------------------------------------------------------- @@ -217,8 +229,8 @@ def build_wheel( # Normalize version for PEP 440 compliance (dashes → local identifier) whl_version = normalize_wheel_version(version) - dist_info = f"periscope-{whl_version}.dist-info" - whl_name = f"periscope-{whl_version}-py3-none-{wheel_tag}.whl" + dist_info = f"{PYPI_DIST_NAME_NORMALIZED}-{whl_version}.dist-info" + whl_name = f"{PYPI_DIST_NAME_NORMALIZED}-{whl_version}-py3-none-{wheel_tag}.whl" output_dir.mkdir(parents=True, exist_ok=True) whl_path = output_dir / whl_name @@ -271,7 +283,7 @@ def _add(arcname: str, data: bytes, unix_mode: int = 0o644) -> None: def _build_metadata(version: str, readme: str | None) -> str: lines = [ "Metadata-Version: 2.1", - "Name: periscope", + f"Name: {PYPI_DIST_NAME}", f"Version: {version}", "Summary: Local web viewer for AI agent sessions", "Home-page: https://github.com/latentsignal-org/periscope", diff --git a/scripts/build_wheels_test.py b/scripts/build_wheels_test.py index 968c0c91a..6878bdc5b 100644 --- a/scripts/build_wheels_test.py +++ b/scripts/build_wheels_test.py @@ -213,7 +213,7 @@ def test_nested_path_in_tar_gz(self, tmp_path: Path) -> None: class TestBuildWheel: def test_wheel_filename_matches_convention(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") - assert whl.name == "periscope-0.15.0-py3-none-manylinux_2_28_x86_64.whl" + assert whl.name == "periscope_agentsview-0.15.0-py3-none-manylinux_2_28_x86_64.whl" def test_wheel_is_valid_zip(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") @@ -226,10 +226,10 @@ def test_wheel_contains_expected_files(self, tmp_path: Path) -> None: assert "periscope/__init__.py" in names assert "periscope/__main__.py" in names assert "periscope/bin/periscope" in names - assert "periscope-0.15.0.dist-info/METADATA" in names - assert "periscope-0.15.0.dist-info/WHEEL" in names - assert "periscope-0.15.0.dist-info/entry_points.txt" in names - assert "periscope-0.15.0.dist-info/RECORD" in names + assert "periscope_agentsview-0.15.0.dist-info/METADATA" in names + assert "periscope_agentsview-0.15.0.dist-info/WHEEL" in names + assert "periscope_agentsview-0.15.0.dist-info/entry_points.txt" in names + assert "periscope_agentsview-0.15.0.dist-info/RECORD" in names def test_binary_has_executable_permissions(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") @@ -264,9 +264,9 @@ def test_windows_wheel_uses_exe_binary(self, tmp_path: Path) -> None: def test_metadata_required_fields(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") with zipfile.ZipFile(whl) as zf: - metadata = zf.read("periscope-0.15.0.dist-info/METADATA").decode() + metadata = zf.read("periscope_agentsview-0.15.0.dist-info/METADATA").decode() assert "Metadata-Version: 2.1" in metadata - assert "Name: periscope" in metadata + assert "Name: periscope-agentsview" in metadata assert "Version: 0.15.0" in metadata assert "Requires-Python: >=3.9" in metadata assert "License: MIT" in metadata @@ -275,21 +275,21 @@ def test_metadata_required_fields(self, tmp_path: Path) -> None: def test_wheel_file_has_root_is_purelib_false(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") with zipfile.ZipFile(whl) as zf: - wheel_meta = zf.read("periscope-0.15.0.dist-info/WHEEL").decode() + wheel_meta = zf.read("periscope_agentsview-0.15.0.dist-info/WHEEL").decode() assert "Root-Is-Purelib: false" in wheel_meta assert "Generator: periscope-build-wheels" in wheel_meta def test_entry_points_correct(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") with zipfile.ZipFile(whl) as zf: - ep = zf.read("periscope-0.15.0.dist-info/entry_points.txt").decode() + ep = zf.read("periscope_agentsview-0.15.0.dist-info/entry_points.txt").decode() assert "[console_scripts]" in ep assert "periscope = periscope:main" in ep def test_record_contains_hashes(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64") with zipfile.ZipFile(whl) as zf: - record = zf.read("periscope-0.15.0.dist-info/RECORD").decode() + record = zf.read("periscope_agentsview-0.15.0.dist-info/RECORD").decode() # Each non-RECORD entry should have a sha256 hash lines = [ln for ln in record.splitlines() if ln.strip()] record_line = None @@ -306,7 +306,7 @@ def test_readme_included_in_metadata(self, tmp_path: Path) -> None: readme = "# periscope\nA great tool." whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64", readme=readme) with zipfile.ZipFile(whl) as zf: - metadata = zf.read("periscope-0.15.0.dist-info/METADATA").decode() + metadata = zf.read("periscope_agentsview-0.15.0.dist-info/METADATA").decode() assert "A great tool." in metadata def test_init_py_uses_execvp_on_unix(self, tmp_path: Path) -> None: @@ -330,12 +330,12 @@ def test_wheel_filename_with_normalized_version(self, tmp_path: Path) -> None: ) assert ( whl.name - == "periscope-0.29.2+periscope.2.258218f5-py3-none-manylinux_2_28_x86_64.whl" + == "periscope_agentsview-0.29.2+periscope.2.258218f5-py3-none-manylinux_2_28_x86_64.whl" ) def test_wheel_filename_darwin_arm64(self, tmp_path: Path) -> None: whl = build_wheel(b"fake", tmp_path, "1.0.0", "darwin_arm64") - assert whl.name == "periscope-1.0.0-py3-none-macosx_11_0_arm64.whl" + assert whl.name == "periscope_agentsview-1.0.0-py3-none-macosx_11_0_arm64.whl" # --------------------------------------------------------------------------- @@ -381,12 +381,12 @@ def test_correct_wheel_names(self, tmp_path: Path) -> None: wheels = build_all_wheels(input_dir, output_dir, "0.15.0") names = {w.name for w in wheels} expected = { - "periscope-0.15.0-py3-none-manylinux_2_28_x86_64.whl", - "periscope-0.15.0-py3-none-manylinux_2_28_aarch64.whl", - "periscope-0.15.0-py3-none-macosx_11_0_x86_64.whl", - "periscope-0.15.0-py3-none-macosx_11_0_arm64.whl", - "periscope-0.15.0-py3-none-win_amd64.whl", - "periscope-0.15.0-py3-none-win_arm64.whl", + "periscope_agentsview-0.15.0-py3-none-manylinux_2_28_x86_64.whl", + "periscope_agentsview-0.15.0-py3-none-manylinux_2_28_aarch64.whl", + "periscope_agentsview-0.15.0-py3-none-macosx_11_0_x86_64.whl", + "periscope_agentsview-0.15.0-py3-none-macosx_11_0_arm64.whl", + "periscope_agentsview-0.15.0-py3-none-win_amd64.whl", + "periscope_agentsview-0.15.0-py3-none-win_arm64.whl", } assert names == expected