Skip to content

emrg: the suite's config-file write guard omits config.toml, the one host file whose corruption stops the daemon #2435

Closed

Description

@how2how2how2-arch

What is observed

tests/conftest.py carries an autouse fixture, _guard_real_config_files, whose purpose is to make any test that writes one of the host's real config files fail immediately. It refuses two files:

_REAL_CONFIG_FILES = (
    (Path.home() / ".emrg" / "projects.yml").resolve(),
    (Path.home() / ".emrg" / "tasks.yml").resolve(),
)

~/.emrg/config.toml — the third, and the only one of the three that the daemon cannot start without — is not there.

Why the omission is not cosmetic

The fixture patches one writer, atomic_write_yaml, in the two modules that hold it. That is the right seam for projects.yml and tasks.yml, which are YAML. config.toml is not YAML and is not written that way: emrg/config.py::ensure_config creates it with

cfg_path.write_text(CONFIG_TEMPLATE, encoding="utf-8")

which atomic_write_yaml never sees. So the file the fixture is named after is covered by neither half of it — not by the file list, and not by the writer it patches.

What that costs

config.toml is the host's live configuration, and a corrupt one is a startup failure rather than a test failure. The recorded shape: a single stray line appended to the end of the file made tomllib raise

TOMLDecodeError: Expected '=' after a key in a key/value pair (at line 48, column 6)

the daemon exited during startup (exit=1), and the traceback landed in ~/.emrg/emrgd-crash.log — a file no startup-failure report reads, so the client's message said only that the daemon had exited and written nothing to its log.

The protection that stands there now is the one a mutation arm removes

Reads are kept off the host file by a separate autouse fixture, _redirect_the_config_path, which repoints config_path() (in emrg.config and in emrg.server.config_reload) at a per-test scratch tree. That redirect is what keeps ensure_config away from the host today.

It is a redirect, not a refusal: it works by making the code resolve a different path. A mutation arm exists precisely to break the code it mutates, so the one thing standing between an arm and the host's config.toml is the thing the arm is designed to disable. Meanwhile the prose rule — a test or an arm must never write this file — is stated in the project's own instructions, and nothing measures it.

Expected

A test that attempts to write the host's ~/.emrg/config.toml fails as loudly, and as immediately, as one that writes projects.yml — and the guard's teeth are demonstrated rather than declared, because a guard that nothing exercises is a claim.

What the fix must satisfy

  1. All three host files are refused, not two.
  2. The seam that actually writes config.toml is covered, not only the YAML one.
  3. Each covered seam has a test that fails when that seam's patch is removed, and passes when it is present.
  4. The guard never makes those files unreadable — a read must pass through untouched.
  5. No test that proves the guard can itself reach a host file: the refusals are driven through paths a test created.
  6. The full suite stays green.

Activity

  1. how2how2how2-arch commented on Oct 21, 2026

    @how2how2how2-arch
    CollaboratorAuthor

    Handled by #2436

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions