diff --git a/ROADMAP.md b/ROADMAP.md index d45addd..c48dc85 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,9 +1,12 @@ # The Booth — roadmap Design: [`docs/design/information-architecture.md`](docs/design/information-architecture.md). -Current version: `0.6.1` (**U1 through U7 landed — every v1 capability is in**; -extracted from eshpfi 2026-09-21). **The 1.0 cut is now a decision, not a -dependency**, and it is the operator's: a major bump needs his approval. +Current version: `1.0.0b1` (**U1 through U7 landed — every v1 capability is +in**; extracted from eshpfi 2026-09-21). **The v1 target is MET and staged as a +beta** (operator, 2026-09-22): feature-complete, external testing, no new +features — the remaining work is bugs. `1.0.0` final is cut when the beta +survives; per the canonical policy an rc would be cut from the same commit, +but a beta may still take fixes. ## v1 target diff --git a/booth/__init__.py b/booth/__init__.py index bdf0cb9..4a807f2 100644 --- a/booth/__init__.py +++ b/booth/__init__.py @@ -1,3 +1,42 @@ -"""The Booth — ephemeral media drop board. See booth.app for the server.""" +"""The Booth — ephemeral media drop board. See booth.app for the server. -__version__ = "0.1.0" +⚠ THIS FILE IS EFFECTIVELY STDLIB-ONLY and nothing used to say so. `scripts/booth` +imports `booth.links` / `booth.marks` / `booth.manifest` under the SYSTEM python3 +with no venv, and importing any of them executes this module first — so a single +third-party import here breaks `booth ask` on every fleet host exactly as one in +those three would. `test_stdlib_only` now covers `__init__` for that reason. +""" + +import tomllib +from pathlib import Path + +_PYPROJECT = Path(__file__).resolve().parent.parent / "pyproject.toml" + + +def _declared_version() -> str: + """The version of the code actually running, read from `pyproject.toml`. + + ⚠ NOT `importlib.metadata`, and the reason is this repo's own shape: there + is no build step and no install step — `booth.service` runs uvicorn with + WorkingDirectory set to the repo, so the running code IS this tree. + Installed metadata describes a DIFFERENT artifact and was found saying + `0.3.0` (a vestigial dist-info, three releases stale, with no package + directory behind it) while the tree was at `1.0.0b1`. A confidently wrong + number that varies by environment is worse than the hardcoded `0.1.0` this + replaced, which at least failed the same way everywhere. + + Falls back to installed metadata for the case this repo does not have but a + consumer might: packaged as a wheel, where pyproject does not ship. + """ + try: + return tomllib.loads(_PYPROJECT.read_text())["project"]["version"] + except (OSError, KeyError, tomllib.TOMLDecodeError): + try: + from importlib.metadata import version + + return version("booth") + except Exception: + return "0.0.0+unknown" + + +__version__ = _declared_version() diff --git a/pyproject.toml b/pyproject.toml index b5b9763..7e950d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "booth" -version = "0.6.1" +version = "1.0.0b1" description = "The Booth — a dead-simple standing web server that scans a data dir of drop-folders and renders each as an ephemeral media 'booth' (image/webm/audio auto-gallery, or a folder's own index.html verbatim). Also accepts browser/curl uploads for pickup under a human-readable id. 24h TTL, then the folder is wiped. Fleet tool for CC sessions to surface A/B and smoke results to the operator." requires-python = ">=3.11" dependencies = [ diff --git a/tests/test_marks.py b/tests/test_marks.py index 0db2e62..38374e3 100644 --- a/tests/test_marks.py +++ b/tests/test_marks.py @@ -7,6 +7,8 @@ See docs/contracts/u2_marks.contract.md. """ import ast import json +import re +import tomllib import pathlib import sys @@ -276,7 +278,7 @@ def test_as_dict_round_trips_through_json(tmp_path): # ---- the stdlib-only invariant (INV-5) -------------------------------------- -@pytest.mark.parametrize("module", ["marks", "asks", "links", "manifest", "benches"]) +@pytest.mark.parametrize("module", ["marks", "asks", "links", "manifest", "benches", "__init__"]) def test_stdlib_only(module): """INV-5. scripts/booth imports these under the system python3 with NO venv, through a `python3 -c` heredoc that no AST extractor can see — so nothing @@ -1428,3 +1430,31 @@ def test_a_healthy_multi_answer_still_hydrates(tmp_path): by = {m.id: m for m in marks_for(tmp_path)} assert by["multi"].error is None, by["multi"].error assert by["single"].error is None, by["single"].error + + +def test_the_package_version_carries_no_literal_of_its_own(): + """`booth.__version__` said `0.1.0` through six releases while pyproject + said `0.6.1` — a second copy of one fact, drifting silently, found only + while cutting 1.0. + + THE ASSERTION IS THE ABSENCE OF A LITERAL, not agreement with pyproject: + `__version__` is now READ from pyproject, so comparing the two would be + circular and would prove only that the read works. The defeating change is + hardcoding a number back into this module, and that is what this catches. + """ + src = (pathlib.Path(__file__).parent.parent / "booth" / "__init__.py").read_text() + literals = re.findall(r'__version__\s*=\s*["\']([^"\']+)["\']', src) + assert not literals, f"booth/__init__.py hardcodes a version again: {literals}" + + +def test_the_package_version_is_the_one_the_tree_declares(): + """And it resolves, from the tree, to what pyproject says — NOT to whatever + a stale dist-info in some venv happens to record. Found saying `0.3.0` from + installed metadata while the tree was at `1.0.0b1`.""" + import booth + + declared = tomllib.loads( + (pathlib.Path(__file__).parent.parent / "pyproject.toml").read_text() + )["project"]["version"] + assert booth.__version__ == declared + assert booth.__version__ != "0.0.0+unknown", "the pyproject read fell through"