Skip to content

Commit c3f3d02

Browse files
Merge pull request #38 from diazMelgarejo/claude/2026-07-30-pypi-package-rename
fix(pypi): publish under periscope-agentsview, not periscope
2 parents 5bd8f75 + 06cf60b commit c3f3d02

5 files changed

Lines changed: 103 additions & 38 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ jobs:
343343
run: |
344344
VERSION=${GITHUB_REF#refs/tags/v}
345345
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)")
346-
pip install "wheels/periscope-${WHL_VERSION}-py3-none-manylinux_2_28_x86_64.whl"
346+
pip install "wheels/periscope_agentsview-${WHL_VERSION}-py3-none-manylinux_2_28_x86_64.whl"
347347
periscope --version
348348
349349
- name: Publish to PyPI

docs/ARCHITECTURE.md

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: Matryoshka model, branch roles, and fork invariants for diazMelgare
77

88
> **Status:** Living document. Update after every upstream replay and fork enhancement.
99
> **Canonical integration branch:** `merged`
10-
> **Last updated:** 2026-07-28
10+
> **Last updated:** 2026-07-30
1111
1212
**Operator policy:** [`docs/guides/periscope-upstream-sync-blueprint.md`](guides/periscope-upstream-sync-blueprint.md)
1313
**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
171171

172172
## Versioning and release tags
173173

174-
Semver pre-release identity:
174+
### Fork release tags (Layer 3 — Periscope identity)
175+
176+
Fork releases use a **grandmother-derived semver** plus a **`-periscope.N`**
177+
suffix that marks the fork’s major AgentsView integrative merge generation on
178+
`merged`. Product binaries and CI artifacts stay branded `periscope`; the
179+
`agentsview` mirror branch name is unrelated to release tags.
180+
181+
**Formula:**
175182

176183
```
177-
v0.(upstream_minor + 1).2-periscope.2
184+
v0.(upstream_minor + 1).2-periscope.N
178185
```
179186

187+
| Part | Meaning |
188+
| --- | --- |
189+
| `v0.(upstream_minor + 1)` | kenn-io/agentsview release minor + 1 (readable upstream base) |
190+
| `.2` | Periscope patch slot on that upstream-derived minor (established convention) |
191+
| `-periscope.N` | Fork identity generation — **N** = major AgentsView → `merged` integrative absorption count |
192+
193+
**Grandmother → fork tag map (canonical examples):**
194+
195+
| Grandmother (`kenn-io/agentsview`) | Fork release tag | Note |
196+
| --- | --- | --- |
197+
| v0.28.x (latentsignal era) | `v0.29.2-periscope.2` | First shipped fork generation |
198+
| v0.39.0 (current grandmother) | **`v0.40.2-periscope.3`** | **Third** major AgentsView integrative merge onto `merged` |
199+
200+
**Current target (post–third AgentsView merge):** `v0.40.2-periscope.3` — bump
201+
`jetbrains-plugin/gradle.properties`, release `-ldflags`, and operator tags when
202+
that integrative merge lands on `merged` (not when the mirror alone advances).
203+
204+
Historical tags (`v0.29.2-periscope.2-*`, `v0.29.2-periscope.3-*`) remain valid
205+
release pointers; do not rewrite them.
206+
207+
### Desktop app semver (parallel track)
208+
209+
`desktop/src-tauri/tauri.conf.json` `version` follows the **grandmother desktop
210+
tree** (currently `0.12.1` on both mirror and `merged`). `productName` on
211+
`merged` is **Periscope**; on the mirror it remains **AgentsView**. Bump desktop
212+
semver when grandmother bumps it during replay — independent of the fork tag
213+
suffix above.
214+
215+
### Commit-suffixed release tags
216+
180217
Tags **always embed the short commit hash** of the release commit. Convention
181218
adopted in `5bd2e8a` (May 2026):
182219

183220
```
184-
v{semver}-{8-char-commit} e.g. v0.29.2-periscope.2-657a1090
221+
v{semver}-{8-char-commit} e.g. v0.40.2-periscope.3-657a1090
185222
```
186223

187224
```bash
188225
COMMIT=$(git rev-parse --short=8 HEAD)
189-
VERSION="v0.29.2-periscope.2"
226+
VERSION="v0.40.2-periscope.3"
190227
git tag -a "${VERSION}-${COMMIT}" -m "Release ${VERSION}-${COMMIT}"
191228
# Push only after operator review — never automatic from tooling here.
192229
```
193230

231+
After each major AgentsView integrative merge onto `merged`:
232+
233+
```bash
234+
# Example: grandmother v0.39.0 → v0.40.2-periscope.3 (third absorption)
235+
OLD_VERSION="v0.29.2-periscope.2"
236+
NEW_VERSION="v0.40.2-periscope.3"
237+
# Update jetbrains-plugin/gradle.properties and release ldflags; then tag as above.
238+
```
239+
194240
---
195241

196242
## Desktop app identity

docs/guides/periscope-modernization-status.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Periscope modernization status
22

3-
**Updated:** 2026-07-28
3+
**Updated:** 2026-07-30
44
**Base commit:** `2b6e5128` (3way modernization replay)
55
**Integration branch:** `merged`
66
**Replaces:** historical `PROGRESS.md` completion ledger (archive only — not active truth)
@@ -35,26 +35,33 @@ related specs.
3535

3636
---
3737

38+
## Fork release tags (Layer 3 — Periscope identity)
39+
40+
See [`ARCHITECTURE.md`](../ARCHITECTURE.md) § Versioning for the full matryoshka
41+
context. Summary:
42+
43+
| Track | Rule |
44+
| --- | --- |
45+
| Fork release tag | `v0.(upstream_minor + 1).2-periscope.N`**N** = major AgentsView → `merged` merge generation |
46+
| Grandmother v0.39.0 | Target **`v0.40.2-periscope.3`** (third integrative absorption) |
47+
| Desktop `tauri.conf.json` | Follows grandmother semver (`0.12.1` today); `productName` **Periscope** on `merged` |
48+
49+
Historical shipped tag: `v0.29.2-periscope.2-*` (grandmother v0.28 era).
50+
3851
## Release tag convention (5bd2e8a)
3952

4053
Fork releases **always embed the short commit hash** of the release commit.
4154
Adopted in commit `5bd2e8a421c326a9a40e73437930ba76f475b9cb` (May 2026).
4255

4356
```
44-
v{semver}-{8-char-commit} e.g. v0.29.2-periscope.2-657a1090
45-
```
46-
47-
Semver pre-release identity on `merged`:
48-
49-
```
50-
v0.(upstream_minor + 1).2-periscope.2
57+
v{semver}-{8-char-commit} e.g. v0.40.2-periscope.3-657a1090
5158
```
5259

5360
Operator tagging (after verification — never automatic from tooling):
5461

5562
```bash
5663
COMMIT=$(git rev-parse --short=8 HEAD)
57-
VERSION="v0.29.2-periscope.2"
64+
VERSION="v0.40.2-periscope.3"
5865
git tag -a "${VERSION}-${COMMIT}" -m "Release ${VERSION}-${COMMIT}"
5966
```
6067

@@ -117,7 +124,7 @@ shell environment overrides (see `desktop/src-tauri/src/lib.rs`).
117124

118125
# Release tag (operator, after verification)
119126
COMMIT=$(git rev-parse --short=8 HEAD)
120-
git tag -a "v0.29.2-periscope.2-${COMMIT}" -m "Release v0.29.2-periscope.2-${COMMIT}"
127+
git tag -a "v0.40.2-periscope.3-${COMMIT}" -m "Release v0.40.2-periscope.3-${COMMIT}"
121128
```
122129

123130
---

scripts/build_wheels.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ def normalize_wheel_version(version: str) -> str:
4444
return version
4545

4646

47+
# ---------------------------------------------------------------------------
48+
# Package identity
49+
# ---------------------------------------------------------------------------
50+
51+
# PyPI project name. The upstream name "periscope" is registered by an
52+
# unrelated project (a subtitle downloader), so this fork publishes under
53+
# a distinct distribution name. The installed console script and the
54+
# internal module/import name stay "periscope" -- only the PyPI project
55+
# identity (and therefore the wheel filename and dist-info dir) differ.
56+
PYPI_DIST_NAME = "periscope-agentsview"
57+
PYPI_DIST_NAME_NORMALIZED = PYPI_DIST_NAME.replace("-", "_")
58+
4759
# ---------------------------------------------------------------------------
4860
# Platform constants
4961
# ---------------------------------------------------------------------------
@@ -217,8 +229,8 @@ def build_wheel(
217229

218230
# Normalize version for PEP 440 compliance (dashes → local identifier)
219231
whl_version = normalize_wheel_version(version)
220-
dist_info = f"periscope-{whl_version}.dist-info"
221-
whl_name = f"periscope-{whl_version}-py3-none-{wheel_tag}.whl"
232+
dist_info = f"{PYPI_DIST_NAME_NORMALIZED}-{whl_version}.dist-info"
233+
whl_name = f"{PYPI_DIST_NAME_NORMALIZED}-{whl_version}-py3-none-{wheel_tag}.whl"
222234

223235
output_dir.mkdir(parents=True, exist_ok=True)
224236
whl_path = output_dir / whl_name
@@ -271,7 +283,7 @@ def _add(arcname: str, data: bytes, unix_mode: int = 0o644) -> None:
271283
def _build_metadata(version: str, readme: str | None) -> str:
272284
lines = [
273285
"Metadata-Version: 2.1",
274-
"Name: periscope",
286+
f"Name: {PYPI_DIST_NAME}",
275287
f"Version: {version}",
276288
"Summary: Local web viewer for AI agent sessions",
277289
"Home-page: https://github.com/latentsignal-org/periscope",

scripts/build_wheels_test.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def test_nested_path_in_tar_gz(self, tmp_path: Path) -> None:
213213
class TestBuildWheel:
214214
def test_wheel_filename_matches_convention(self, tmp_path: Path) -> None:
215215
whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64")
216-
assert whl.name == "periscope-0.15.0-py3-none-manylinux_2_28_x86_64.whl"
216+
assert whl.name == "periscope_agentsview-0.15.0-py3-none-manylinux_2_28_x86_64.whl"
217217

218218
def test_wheel_is_valid_zip(self, tmp_path: Path) -> None:
219219
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:
226226
assert "periscope/__init__.py" in names
227227
assert "periscope/__main__.py" in names
228228
assert "periscope/bin/periscope" in names
229-
assert "periscope-0.15.0.dist-info/METADATA" in names
230-
assert "periscope-0.15.0.dist-info/WHEEL" in names
231-
assert "periscope-0.15.0.dist-info/entry_points.txt" in names
232-
assert "periscope-0.15.0.dist-info/RECORD" in names
229+
assert "periscope_agentsview-0.15.0.dist-info/METADATA" in names
230+
assert "periscope_agentsview-0.15.0.dist-info/WHEEL" in names
231+
assert "periscope_agentsview-0.15.0.dist-info/entry_points.txt" in names
232+
assert "periscope_agentsview-0.15.0.dist-info/RECORD" in names
233233

234234
def test_binary_has_executable_permissions(self, tmp_path: Path) -> None:
235235
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:
264264
def test_metadata_required_fields(self, tmp_path: Path) -> None:
265265
whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64")
266266
with zipfile.ZipFile(whl) as zf:
267-
metadata = zf.read("periscope-0.15.0.dist-info/METADATA").decode()
267+
metadata = zf.read("periscope_agentsview-0.15.0.dist-info/METADATA").decode()
268268
assert "Metadata-Version: 2.1" in metadata
269-
assert "Name: periscope" in metadata
269+
assert "Name: periscope-agentsview" in metadata
270270
assert "Version: 0.15.0" in metadata
271271
assert "Requires-Python: >=3.9" in metadata
272272
assert "License: MIT" in metadata
@@ -275,21 +275,21 @@ def test_metadata_required_fields(self, tmp_path: Path) -> None:
275275
def test_wheel_file_has_root_is_purelib_false(self, tmp_path: Path) -> None:
276276
whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64")
277277
with zipfile.ZipFile(whl) as zf:
278-
wheel_meta = zf.read("periscope-0.15.0.dist-info/WHEEL").decode()
278+
wheel_meta = zf.read("periscope_agentsview-0.15.0.dist-info/WHEEL").decode()
279279
assert "Root-Is-Purelib: false" in wheel_meta
280280
assert "Generator: periscope-build-wheels" in wheel_meta
281281

282282
def test_entry_points_correct(self, tmp_path: Path) -> None:
283283
whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64")
284284
with zipfile.ZipFile(whl) as zf:
285-
ep = zf.read("periscope-0.15.0.dist-info/entry_points.txt").decode()
285+
ep = zf.read("periscope_agentsview-0.15.0.dist-info/entry_points.txt").decode()
286286
assert "[console_scripts]" in ep
287287
assert "periscope = periscope:main" in ep
288288

289289
def test_record_contains_hashes(self, tmp_path: Path) -> None:
290290
whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64")
291291
with zipfile.ZipFile(whl) as zf:
292-
record = zf.read("periscope-0.15.0.dist-info/RECORD").decode()
292+
record = zf.read("periscope_agentsview-0.15.0.dist-info/RECORD").decode()
293293
# Each non-RECORD entry should have a sha256 hash
294294
lines = [ln for ln in record.splitlines() if ln.strip()]
295295
record_line = None
@@ -306,7 +306,7 @@ def test_readme_included_in_metadata(self, tmp_path: Path) -> None:
306306
readme = "# periscope\nA great tool."
307307
whl = build_wheel(b"fake", tmp_path, "0.15.0", "linux_amd64", readme=readme)
308308
with zipfile.ZipFile(whl) as zf:
309-
metadata = zf.read("periscope-0.15.0.dist-info/METADATA").decode()
309+
metadata = zf.read("periscope_agentsview-0.15.0.dist-info/METADATA").decode()
310310
assert "A great tool." in metadata
311311

312312
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:
330330
)
331331
assert (
332332
whl.name
333-
== "periscope-0.29.2+periscope.2.258218f5-py3-none-manylinux_2_28_x86_64.whl"
333+
== "periscope_agentsview-0.29.2+periscope.2.258218f5-py3-none-manylinux_2_28_x86_64.whl"
334334
)
335335

336336
def test_wheel_filename_darwin_arm64(self, tmp_path: Path) -> None:
337337
whl = build_wheel(b"fake", tmp_path, "1.0.0", "darwin_arm64")
338-
assert whl.name == "periscope-1.0.0-py3-none-macosx_11_0_arm64.whl"
338+
assert whl.name == "periscope_agentsview-1.0.0-py3-none-macosx_11_0_arm64.whl"
339339

340340

341341
# ---------------------------------------------------------------------------
@@ -381,12 +381,12 @@ def test_correct_wheel_names(self, tmp_path: Path) -> None:
381381
wheels = build_all_wheels(input_dir, output_dir, "0.15.0")
382382
names = {w.name for w in wheels}
383383
expected = {
384-
"periscope-0.15.0-py3-none-manylinux_2_28_x86_64.whl",
385-
"periscope-0.15.0-py3-none-manylinux_2_28_aarch64.whl",
386-
"periscope-0.15.0-py3-none-macosx_11_0_x86_64.whl",
387-
"periscope-0.15.0-py3-none-macosx_11_0_arm64.whl",
388-
"periscope-0.15.0-py3-none-win_amd64.whl",
389-
"periscope-0.15.0-py3-none-win_arm64.whl",
384+
"periscope_agentsview-0.15.0-py3-none-manylinux_2_28_x86_64.whl",
385+
"periscope_agentsview-0.15.0-py3-none-manylinux_2_28_aarch64.whl",
386+
"periscope_agentsview-0.15.0-py3-none-macosx_11_0_x86_64.whl",
387+
"periscope_agentsview-0.15.0-py3-none-macosx_11_0_arm64.whl",
388+
"periscope_agentsview-0.15.0-py3-none-win_amd64.whl",
389+
"periscope_agentsview-0.15.0-py3-none-win_arm64.whl",
390390
}
391391
assert names == expected
392392

0 commit comments

Comments
 (0)