fix(issue-26): the wrapper reads .mam.env through the Python loader's parser, never source

- lib_py/delegation/envfile.py (stdlib only) holds the one set of rules:
  comments and blank lines skipped, key and value stripped, one matching
  quote pair removed, no shell interpretation, keys must match
  ^[A-Za-z_][A-Za-z0-9_]*$ (bad or `=`-less lines warn with the line
  number, never the value).
- mqtt_common._parse_env_file delegates to envfile.apply.
- The delegate-job wrapper runs envfile.py --shell by path and evals the
  shlex-quoted exports; values never reach a command line. The environment
  now wins over the file in both loaders.
- .mam.env.example: commented assignments no longer use $HOME, which the
  parser keeps literally.

The shell parsed the env file in one place only (wrapper :38), not three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-09-30 15:47:40 +00:00
1 parent e1eb22bd59
commit f1331d36ce
5 files changed
+345 -19

No files matched your search

@@ -31,6 +31,8 @@ from typing import Any, Callable, Dict, Iterable, List, Optional
import paho.mqtt.client as mqtt
from lib_py.delegation import envfile
logger = logging.getLogger("delegate_job.mqtt_common")
_warned_deprecated_env = False
@@ -94,20 +96,9 @@ def _load_dotenv(workspace_dir: Optional[str] = None) -> None:
_parse_env_file(legacy_env_path)
def _parse_env_file(path: str) -> None:
try:
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
key, _, val = line.partition("=")
key = key.strip()
val = val.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = val
except Exception as e:
logger.warning("Failed to parse env file %s: %s", path, e)
"""Issue #26: the rules live in envfile.py, shared with the wrapper."""
for warning in envfile.apply(path, os.environ)[1]:
logger.warning("%s", warning)
_env_loaded = False