Skip to content

Stray empty files (echo, === learning-log.jsonl (line count) ===) created in repo root by malformed agent shell command (zsh MULTIOS) #234

Description

@dean0x

Summary

Two empty (0-byte) files were found untracked in the repository root, created at the same instant (2026-06-01 15:12:52):

  • === learning-log.jsonl (line count) ===
  • echo

They are harmless (no content, no effect on build/tests/CI) but they pollute the repo root and one was nearly committed. We should find the source and harden against it.

Evidence

  • Both files are 0 bytes with an identical birth timestamp → created by a single shell command, not two actions.
  • The two names reconstruct an intended command of the form:
    echo "=== learning-log.jsonl (line count) ===" && wc -l .devflow/learning/learning-log.jsonl
    i.e. print a header label, then count lines of the learning log.
  • Not in interactive history: grep of ~/.zsh_history for the pattern returns nothing → the command ran in a non-interactive shell (an agent/subprocess Bash call, e.g. a spawned claude -p hook or a Claude Code session), which does not log to ~/.zsh_history.

Mechanism (how empty files appeared instead of output)

The shell is zsh, which has MULTIOS enabled by default. When a line is parsed as redirections with no surviving command word — e.g. the command got mangled into:

> "=== learning-log.jsonl (line count) ===" > echo

zsh creates an empty file per redirect target. That exactly matches the observed signature (two 0-byte files, same timestamp). The intended echo/wc output never ran; the words echo and the header string were consumed as filenames.

What this is NOT

Searched the full source + installed hooks + all skills/commands/agents:

  • scripts/hooks/, ~/.devflow/scripts/hooks/, src/, shared/, plugins/, ~/.claude/{skills,commands,agents}/
  • No file emits the === … (line count) === header string.
  • The only learning-log line-count construction is scripts/hooks/eval-learning:40:
    _LEARN_OBS_TOTAL=$(wc -l < "$LEARNING_DIR/learning-log.jsonl" | tr -d ' ')
    which is correctly quoted with < redirection and prints no header. It is not the culprit.

So the deterministic learning hook did not create these. The header style strongly suggests an LLM/agent-generated Bash command (ad-hoc terminal inspection of the learning log) that was malformed, run in a non-interactive shell — possibly a spawned sub-agent (sidecar/learning/knowledge claude -p) or a main-session agent during the Jun 1 sidecar/init work.

Why it still matters

  • Even if the trigger was an ad-hoc agent command, devflow agents/hooks run a lot of Bash in non-interactive zsh. A mangled redirection silently creating stray files in the project root is a real artifact-pollution failure mode worth guarding.

Suggested follow-up

  1. Reproduce / locate the source: audit agent + hook Bash invocations that inspect learning-log.jsonl (line counts, cat/wc with echo "=== … ===" headers). Check whether any prompt/template instructs constructing such a header that could be fed into a redirection.
  2. Harden: prefer printf/quoted heredocs over echo "…" > target patterns; never build commands where a quoted header could land adjacent to a >.
  3. Detect: consider a lightweight guard (gitignore pattern and/or a hook check) so stray top-level files like echo / === … === are caught early rather than reaching git status.
  4. Confirm zsh MULTIOS theory: validate that the observed empty-file pair is reproducible from a mangled multi-redirect line in this environment.

Affected

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions