Repository navigation
S1 — appwrite_env logs before it raises (C-71) #182
Description
Part of epic #181. Closes register C-71 (Tier 4).
Background
ADR-008:48 requires that "raised structural failures must be logged at ERROR level or higher", and :51 that "raising is not a substitute for logging." C-19 swept the whole repo for this in June — 23 of 24 raises were fixed.
views_postprocessing/unfao/appwrite_env.py was written later (þing-01 P1, #134) and predates nothing — it simply never got the convention. assert_env_declared raises EnvironmentError naming every missing variable, but writes no log record.
How this was found, and why it matters more than a Tier 4 usually would. contract/launch_config.py was written in #149 to deliberately mirror this module — same shape, same failure style, same dependency-light constraint. It inherited the flaw, the S1 diff review caught it in the new code, and the new module was fixed to log-before-raise. The old one was left alone for scope discipline.
So the pair is now inconsistent in exactly the wrong direction: the module written to match the pattern is the one that has it. The next entry-validation module will be written against whichever of the two its author opens first.
Work
views_postprocessing/unfao/appwrite_env.py:
- Add a module-level
logger = logging.getLogger(__name__), matchinglaunch_config.py. - In
assert_env_declared, build the message into a local,logger.error(...)it, then raise it — the same three-line shape aslaunch_config.assert_contract_mode, including the# ADR-008: logged persistently AND raisedcomment.
Security constraint, non-negotiable. CONNECTION_ENV contains APPWRITE_DATASTORE_API_KEY, a secret slot. The existing message names variables, never values — missing is a list of names produced by os.getenv(name) being falsy. Adding a log call must not change that. Do not log os.environ, do not log the resolved values, do not add a "current environment" dump for debuggability. tests/test_redaction_guard.py governs this repo's log surface; it must stay green.
Do NOT extract a shared helper between appwrite_env and launch_config. See the epic's WET-before-DRY boundary — the named trigger for revisiting is a third such module.
Acceptance criteria
assert_env_declaredlogs atERRORbefore raising; message content is unchanged apart from being captured in a local first.- No secret value can reach a log record from this function.
- A test asserts log-before-raise for both modules, so the pair cannot diverge again.
Testing
New or extended test (suggested home: tests/test_env_declaration.py, which already governs this module):
def test_both_entry_validators_log_before_raising(caplog):
"""ADR-008:48/51 — a structural refusal must leave a persistent record."""- Use
caplog.at_level(logging.ERROR); assert exactly one ERROR record from each module's refusal path. - Assert the record names the missing variable and does not contain any value from
os.environ. - Parametrise across
appwrite_env.assert_env_declaredandlaunch_config.assert_contract_modeso a future third module is a one-line addition rather than a rewrite.
Validation:
ruff check .
pytest -q tests/test_env_declaration.py tests/test_launch_config.py tests/test_redaction_guard.py
pytest -q
Dependencies
None. This is the smallest change in the epic and touches nothing else.
Files
views_postprocessing/unfao/appwrite_env.py:44-51— the fixviews_postprocessing/contract/launch_config.py:52-70— the pattern to matchtests/test_env_declaration.py,tests/test_redaction_guard.py— the governing testsdocs/ADRs/008_*.md:48,51— the requirement
Activity
fix(env): S1 #182 — appwrite_env logs before it raises, and a guard o…
Polichinel commentedon Aug 1, 2026
Landed in #193 (squashed to development).
appwrite_env.assert_env_declaredlogs at ERROR before raising; the ADR-008 obligation is parametrised over both entry validators so the pair cannot drift again. No shared helper — WET before DRY, named trigger is a third such module.Mutation-tested: removing the
logger.errorline fails 2 of the new tests.Review findings F2 (this diff falsified the test file's own module docstring) and F3 (undeclared log side effect) fixed in the same PR. F1 became C-74 / #192.
chore(register): close C-71 — fixed by S1 this morning, filed Open al…