|
| 1 | +--- |
| 2 | +title: v1.2.1 PyPI Publish + Tag + GitHub Release |
| 3 | +date: 2026-04-28 |
| 4 | +status: Plan ready, execution pending (deferred to fresh session) |
| 5 | +target_release: v1.2.1 |
| 6 | +preceded_by: docs/specs/2026-04-28-v1.2.1-duplicate-refine-race.md |
| 7 | +--- |
| 8 | + |
| 9 | +# v1.2.1 PyPI Publish — Step-by-Step |
| 10 | + |
| 11 | +## Goal |
| 12 | + |
| 13 | +Cut the v1.2.1 release across PyPI, git tags, and GitHub. Skip v1.1.0 |
| 14 | +and v1.2.0 PyPI uploads — they are git-tag-only milestones; PyPI for |
| 15 | +the v1.x line starts at v1.2.1. |
| 16 | + |
| 17 | +## Pre-flight state (verified 2026-04-28 end of day) |
| 18 | + |
| 19 | +- `origin/main` == local main == `e7cfdba` (8 commits ahead of dawn-of-day) |
| 20 | +- Test suite: **545 pass**, 2 skipped, 3 deselected |
| 21 | +- Worktrees: clean (only `C:/Projeler/mnemos` main + `C:/Projeler/mnemos-v1.1` editable-install) |
| 22 | +- Local branches: clean (`main`, `feat/identity-bootstrap-limit`, `feature/v1.1.0`, `legacy/atomic-paradigm`) |
| 23 | +- Existing tags: `v0.2.0`, `v0.3.0..v0.3.3`, `v0.4.0-archived`, `v1.0.0a1`, `v1.1.0` (no v1.2.x) |
| 24 | +- `dist/`: contains stale `mnemos_dev-1.1.0-*` from 2026-04-26; v1.2.x not yet built |
| 25 | +- `PYPI_TOKEN` in Windows User env: set (per CLAUDE.md inventory) |
| 26 | + |
| 27 | +## Pre-flight bumps still required |
| 28 | + |
| 29 | +These are step 1 below. Listing here so the new-session resume can |
| 30 | +verify before invoking the plan: |
| 31 | + |
| 32 | +- `pyproject.toml:version` is `1.1.0` → must bump to `1.2.1` |
| 33 | +- `mnemos/__init__.py:__version__` is `"1.0.0a1"` (stale since v1.0 |
| 34 | + alpha era; never touched in v1.1 or v1.2.0/v1.2.1) → must bump to |
| 35 | + `"1.2.1"` |
| 36 | + |
| 37 | +## Why skip v1.1.0 + v1.2.0 PyPI? |
| 38 | + |
| 39 | +The pre-existing `dist/mnemos_dev-1.1.0-*.whl` was built on 2026-04-26. |
| 40 | +Since then the codebase landed: |
| 41 | + |
| 42 | +- v1.2.0 locale-aware output schema |
| 43 | +- v1.2.1 refine-pipeline race fix (per-JSONL filelock) |
| 44 | +- v1.2.1 identity isolation fix (3 same-day bugs surfaced by bootstrap pilot) |
| 45 | + |
| 46 | +Publishing the 1.1.0 wheel now would commit to PyPI a snapshot that |
| 47 | +predates the duplicate-refine bug fix, which is exactly the regression |
| 48 | +v1.2.1 closes. Better story: PyPI v1.x line starts at v1.2.1 with |
| 49 | +"all known bugs fixed". v1.1.0 + v1.2.0 are git-tag milestones in the |
| 50 | +narrative; users coming from v0.3.3 via `pip install --upgrade |
| 51 | +mnemos-dev` jump straight to v1.2.1. |
| 52 | + |
| 53 | +(Both versions are documented in CHANGELOG.md regardless. Tags |
| 54 | +`v1.1.0` and `v1.2.0` will live on GitHub.) |
| 55 | + |
| 56 | +## Steps |
| 57 | + |
| 58 | +### 1. Version bumps |
| 59 | + |
| 60 | +```bash |
| 61 | +# pyproject.toml |
| 62 | +sed -i 's/^version = "1.1.0"/version = "1.2.1"/' pyproject.toml |
| 63 | + |
| 64 | +# mnemos/__init__.py — replace whatever __version__ is there |
| 65 | +sed -i 's/^__version__ = ".*"/__version__ = "1.2.1"/' mnemos/__init__.py |
| 66 | + |
| 67 | +# Verify |
| 68 | +grep -E "^version|^__version__" pyproject.toml mnemos/__init__.py |
| 69 | +# Expect: |
| 70 | +# pyproject.toml:version = "1.2.1" |
| 71 | +# mnemos/__init__.py:__version__ = "1.2.1" |
| 72 | +``` |
| 73 | + |
| 74 | +### 2. Sanity test |
| 75 | + |
| 76 | +```bash |
| 77 | +cd C:/Projeler/mnemos-v1.1 |
| 78 | +python -m pytest tests/ -q |
| 79 | +# Expect: 545 passed, 2 skipped, 3 deselected |
| 80 | +``` |
| 81 | + |
| 82 | +If a test fails, STOP — do not proceed to publish. Investigate first. |
| 83 | + |
| 84 | +### 3. Build |
| 85 | + |
| 86 | +```bash |
| 87 | +# Clean dist/ first so we're not contaminated by 2026-04-26's v1.1.0 wheel |
| 88 | +mv dist/mnemos_dev-1.1.0-py3-none-any.whl dist/_archive_v1.1.0.whl 2>/dev/null |
| 89 | +mv dist/mnemos_dev-1.1.0.tar.gz dist/_archive_v1.1.0.tar.gz 2>/dev/null |
| 90 | +# (rename instead of delete so the v1.1.0 artifact is preserved on disk |
| 91 | +# in case we ever decide to publish historically) |
| 92 | + |
| 93 | +python -m build |
| 94 | +# Expect: dist/mnemos_dev-1.2.1-py3-none-any.whl (~120 KB) |
| 95 | +# dist/mnemos_dev-1.2.1.tar.gz (~660 KB) |
| 96 | +``` |
| 97 | + |
| 98 | +### 4. Smoke test in clean venv |
| 99 | + |
| 100 | +```bash |
| 101 | +# Throwaway venv to validate the wheel installs cleanly |
| 102 | +python -m venv C:/tmp/mnemos-smoke-v1.2.1 |
| 103 | +C:/tmp/mnemos-smoke-v1.2.1/Scripts/pip install C:/Projeler/mnemos-v1.1/dist/mnemos_dev-1.2.1-py3-none-any.whl |
| 104 | +C:/tmp/mnemos-smoke-v1.2.1/Scripts/mnemos --version |
| 105 | +# Expect: mnemos 1.2.1 (or similar; exact format depends on how cli.py |
| 106 | +# prints the version) |
| 107 | +C:/tmp/mnemos-smoke-v1.2.1/Scripts/mnemos --help | head -5 |
| 108 | +# Expect: usage: mnemos [-h] {init,...} (no exception, no missing modules) |
| 109 | +``` |
| 110 | + |
| 111 | +If the smoke fails (missing module, import error, version mismatch), |
| 112 | +STOP and fix before publishing. The smoke is the last reversible |
| 113 | +check before twine upload burns the version number. |
| 114 | + |
| 115 | +Cleanup smoke venv after: |
| 116 | +```bash |
| 117 | +python -c "import shutil; shutil.rmtree('C:/tmp/mnemos-smoke-v1.2.1')" |
| 118 | +``` |
| 119 | + |
| 120 | +### 5. Twine upload to PyPI (IRREVERSIBLE) |
| 121 | + |
| 122 | +```bash |
| 123 | +python -m twine upload C:/Projeler/mnemos-v1.1/dist/mnemos_dev-1.2.1* |
| 124 | +# Username: __token__ (literal string, twine convention for tokens) |
| 125 | +# Password: <value of $env:PYPI_TOKEN> |
| 126 | +``` |
| 127 | + |
| 128 | +Twine will print the URL on success: `https://pypi.org/project/mnemos-dev/1.2.1/`. |
| 129 | + |
| 130 | +If the upload fails with "File already exists", the version was |
| 131 | +inadvertently published earlier. Check pypi.org directly. If you need |
| 132 | +to re-upload (e.g., for a bad wheel), bump to 1.2.2 — PyPI does NOT |
| 133 | +allow overwriting a version, only yanking. |
| 134 | + |
| 135 | +### 6. Git tags (annotated) |
| 136 | + |
| 137 | +```bash |
| 138 | +cd C:/Projeler/mnemos |
| 139 | +git tag v1.2.0 -a -m "v1.2.0 — Locale-Aware Output (git-tag milestone, not on PyPI)" 4fddca4 |
| 140 | +git tag v1.2.1 -a -m "v1.2.1 — Refine race + identity isolation hot-fix" e7cfdba |
| 141 | +git push origin v1.2.0 v1.2.1 |
| 142 | +``` |
| 143 | + |
| 144 | +### 7. GitHub release |
| 145 | + |
| 146 | +```bash |
| 147 | +cd C:/Projeler/mnemos |
| 148 | +gh release create v1.2.1 \ |
| 149 | + --title "v1.2.1 — Refine race + identity isolation hot-fix" \ |
| 150 | + --notes-file CHANGELOG.md \ |
| 151 | + C:/Projeler/mnemos-v1.1/dist/mnemos_dev-1.2.1-py3-none-any.whl \ |
| 152 | + C:/Projeler/mnemos-v1.1/dist/mnemos_dev-1.2.1.tar.gz |
| 153 | +``` |
| 154 | + |
| 155 | +`--notes-file CHANGELOG.md` includes the entire CHANGELOG; this is |
| 156 | +intentional — release notes that show the full project history aid |
| 157 | +discoverability for new users finding the repo. |
| 158 | + |
| 159 | +(If the user prefers the release notes to be just the v1.2.1 entry, |
| 160 | +extract it manually from CHANGELOG.md and pass `--notes "<body>"` instead.) |
| 161 | + |
| 162 | +### 8. Commit the version bumps |
| 163 | + |
| 164 | +```bash |
| 165 | +cd C:/Projeler/mnemos-v1.1 |
| 166 | +git add pyproject.toml mnemos/__init__.py |
| 167 | +git commit -m "chore(release): bump version to 1.2.1" |
| 168 | +git push origin feat/identity-bootstrap-limit # or main, depending on workflow |
| 169 | +``` |
| 170 | + |
| 171 | +If working from `feat/identity-bootstrap-limit`, fast-forward main |
| 172 | +afterward: |
| 173 | +```bash |
| 174 | +cd C:/Projeler/mnemos |
| 175 | +git merge --ff-only feat/identity-bootstrap-limit |
| 176 | +git push origin main |
| 177 | +``` |
| 178 | + |
| 179 | +### 9. STATUS / ROADMAP update |
| 180 | + |
| 181 | +After successful publish, update STATUS.md and ROADMAP.md so the |
| 182 | +next session's resume protocol surfaces the right "next work": |
| 183 | + |
| 184 | +- STATUS.md: move v1.2.1 from "in progress" to "shipped to PyPI"; |
| 185 | + remove the publish-related items from "Pending user actions"; |
| 186 | + add a brief "Released 2026-04-28" line under the version banner. |
| 187 | +- ROADMAP.md: flip the v1.2.1 PyPI column from `—` to `✅` in the |
| 188 | + Version status table. |
| 189 | + |
| 190 | +Commit: |
| 191 | +```bash |
| 192 | +git add STATUS.md docs/ROADMAP.md |
| 193 | +git commit -m "docs: v1.2.1 published to PyPI" |
| 194 | +git push origin main |
| 195 | +``` |
| 196 | + |
| 197 | +## Definition of done |
| 198 | + |
| 199 | +- [ ] `pyproject.toml` and `mnemos/__init__.py` show 1.2.1 |
| 200 | +- [ ] `pytest tests/ -q` — 545 passed |
| 201 | +- [ ] `dist/mnemos_dev-1.2.1-py3-none-any.whl` and `.tar.gz` exist |
| 202 | +- [ ] Smoke install in clean venv: `mnemos --version` prints 1.2.1 |
| 203 | +- [ ] `pip search mnemos-dev` (or pypi.org page) shows 1.2.1 |
| 204 | +- [ ] `git tag` lists `v1.2.0` and `v1.2.1` |
| 205 | +- [ ] `git ls-remote origin "refs/tags/v1.2.*"` confirms remote tags |
| 206 | +- [ ] GitHub releases page shows `v1.2.1` with the two artifacts |
| 207 | + attached |
| 208 | +- [ ] STATUS / ROADMAP updated and pushed |
| 209 | + |
| 210 | +## Rollback / damage control |
| 211 | + |
| 212 | +PyPI does not allow deleting a published version. Options if v1.2.1 |
| 213 | +turns out broken: |
| 214 | + |
| 215 | +1. **Yank** the version: `pip install twine; twine yank mnemos-dev==1.2.1 |
| 216 | + --reason "<short>"` — keeps the version string visible on PyPI but |
| 217 | + prevents new installs from picking it. Existing users with 1.2.1 |
| 218 | + installed are unaffected. |
| 219 | +2. **Ship v1.2.2** with the fix. This is the standard PyPI workflow |
| 220 | + (no overwrite). |
| 221 | + |
| 222 | +For git tags: `git push --delete origin v1.2.1; git tag -d v1.2.1` if |
| 223 | +the tag pointed at a bad commit. Be cautious — anyone who already |
| 224 | +fetched the tag has the old reference. |
| 225 | + |
| 226 | +## Resume protocol — what to do when this plan is picked up |
| 227 | + |
| 228 | +The new session's `mnemos` resume should land here. Steps for the |
| 229 | +agent (not the user): |
| 230 | + |
| 231 | +1. Read this file top-to-bottom. |
| 232 | +2. Verify pre-flight state still matches: |
| 233 | + - `git status --short` clean |
| 234 | + - `git rev-parse HEAD` == latest documented in STATUS |
| 235 | + - `python -m pytest tests/ -q` still 545 pass |
| 236 | +3. Work step-by-step. Pause AFTER step 4 (smoke test) and confirm |
| 237 | + with the user before step 5 (twine upload) — that is the |
| 238 | + irreversible boundary. |
| 239 | +4. After step 7 (GitHub release), continue with step 8 (commit |
| 240 | + bumps) and step 9 (STATUS/ROADMAP) without further confirmation |
| 241 | + — those are routine cleanup. |
| 242 | + |
| 243 | +Estimated wall time: 15-25 min total, dominated by `python -m build` |
| 244 | +(~30s), twine upload (~30s), and waiting for the user to type the |
| 245 | +PyPI token at step 5. |
0 commit comments