From 6e732ab7141fc26ec6b3b4dc059124b3725467bf Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Mon, 27 Apr 2026 06:03:46 -0700 Subject: [PATCH] fix(build): ship lib/ subpackage + extend drift gate to SUBPACKAGES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two compounding bugs that bit hermes (and any other workspace that reaches main.py:142): 1. workspace/lib/ was in EXCLUDE_DIRS so the published wheel didn't contain the directory at all. main.py imports `from lib.pre_stop import read_snapshot` (and `build_snapshot`, `write_snapshot`) so every workspace startup that reaches the snapshot path crashed with `ModuleNotFoundError: No module named 'lib'`. 2. Even if lib/ had shipped, `lib` wasn't in SUBPACKAGES so the import-rewriter would have left the bare `from lib.pre_stop` unqualified — it would still fail because the package would only be reachable as `molecule_runtime.lib`. Fix: move `lib` from EXCLUDE_DIRS to SUBPACKAGES (one entry each). Drift gate extension: the existing gate I added in #2163 only asserted TOP_LEVEL_MODULES against workspace/*.py. This change adds the symmetric assertion for SUBPACKAGES against workspace// (filtered by EXCLUDE_DIRS + presence of __init__.py). Catches both: - Subpackage added to workspace/ but missed in SUBPACKAGES - Subpackage missing from workspace/ but lingering in SUBPACKAGES - Subpackage wrongly in EXCLUDE_DIRS while also referenced by rewritten imports (the lib case) Tested locally: build of 0.1.99 now ships lib/ and main.py contains `from molecule_runtime.lib.pre_stop import ...` correctly rewritten. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/build_runtime_package.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/scripts/build_runtime_package.py b/scripts/build_runtime_package.py index 66342bd7..967ed3ac 100755 --- a/scripts/build_runtime_package.py +++ b/scripts/build_runtime_package.py @@ -82,6 +82,7 @@ TOP_LEVEL_MODULES = { SUBPACKAGES = { "adapters", "builtin_tools", + "lib", "plugins_registry", "policies", "skill_loader", @@ -105,8 +106,7 @@ EXCLUDE_FILES = { EXCLUDE_DIRS = { "__pycache__", "tests", - "lib", - "molecule_audit", + "molecule_audit", # only used by tests; not on production import path "scripts", } @@ -275,6 +275,32 @@ def main() -> int: print(" Edit scripts/build_runtime_package.py:TOP_LEVEL_MODULES to match.", file=sys.stderr) return 3 + # Same drift gate for SUBPACKAGES — catches the inverse class of + # bug where a workspace/ subdirectory is referenced by main.py + # (`from lib.pre_stop import ...`) but is either missing from + # SUBPACKAGES (so the rewriter doesn't qualify the import) or + # accidentally listed in EXCLUDE_DIRS (so the directory itself + # isn't shipped). 0.1.16-0.1.19 had `lib` in EXCLUDE_DIRS while + # main.py imported from it — `ModuleNotFoundError: No module + # named 'lib'` at every workspace startup. + on_disk_subpkgs = { + d.name for d in src.iterdir() + if d.is_dir() + and d.name not in EXCLUDE_DIRS + and d.name not in {"__pycache__"} + and (d / "__init__.py").exists() + } + sub_missing = on_disk_subpkgs - SUBPACKAGES + sub_stale = SUBPACKAGES - on_disk_subpkgs + if sub_missing or sub_stale: + print("error: SUBPACKAGES drifted from workspace/ subdirectories:", file=sys.stderr) + if sub_missing: + print(f" in workspace/ but NOT in SUBPACKAGES (will ship un-rewritten or be excluded): {sorted(sub_missing)}", file=sys.stderr) + if sub_stale: + print(f" in SUBPACKAGES but NOT in workspace/ (no-op, but misleading): {sorted(sub_stale)}", file=sys.stderr) + print(" Edit scripts/build_runtime_package.py:SUBPACKAGES + EXCLUDE_DIRS to match.", file=sys.stderr) + return 3 + pkg_dir = out / "molecule_runtime" print(f"[build] source: {src}") print(f"[build] output: {out}")