Skip to content

bug: outbox silently drops directory entries (e.g. guides/) — silent loss of intended deliveries on every session #524

@nathanschram

Description

Audit-filed by untether-staging monitor loop, pass 1, run 20260513T015418Z, at 2026-05-13T01:54Z.

Source: untether.service on lba-1, 0.35.3rc13.

Type: Enhancement opportunity (ENH-PATCH)

Observation:
On 2026-05-13 at 11:54:46 AEST, after a successful Claude Code run in chat -5173025382 (project auditor-toolkit, session 4e15a844-0632-477c-a9c3-c0be07573de0, 7 turns, $3.44), the outbox delivery scan logged:

2026-05-13T01:54:46.022294Z [info] outbox.skipped
  branch=None chat_id=-5173025382
  cwd=/home/nathan/claude-code-tools/lba/tools/auditor-toolkit/main
  engine=claude project=auditor-toolkit
  resume=4e15a844-0632-477c-a9c3-c0be07573de0
  skipped=[('guides', 'directory')]
  user_msg_id=51258

The agent had placed an item named guides (a directory) into .untether-outbox/. The outbox delivery code (outbox_delivery.py) treated directories as unsupported and dropped the entry. No surface was given to the user — the structured log is the only trace. The user finishing the run never learned the agent intended to deliver a folder.

Opportunity:
Either:

  • (A) Surface the skipped item to the user. Append a single line to the final message footer: 📎 skipped (unsupported): guides (directory) or include it inline near the existing 📎 attachments block. Tells the user something was intended, prompts the agent to re-attempt with a supported approach (zip/tarball, individual files).
  • (B) Auto-zip directories. On encountering a directory in the outbox, transparently zip -r guides.zip guides/ and deliver as a single archive. Add a size cap (outbox_max_files already exists — add outbox_max_archive_bytes) and abort to (A) if too large.

(A) is cheap and unambiguous; (B) is a richer feature but adds packaging logic and a new failure surface (oversized archives, deny-globbed files inside the directory tree).

Why it matters:

  • User impact: anyone whose agent generates a folder of outputs (docs, screenshots, exported reports). With workspace-mode and longer Claude/Codex runs becoming standard, agents producing folder structures is increasingly natural.
  • Frequency: occasional today — agents have to be specifically prompted to deliver folders. Will rise as users get more comfortable asking agents to produce multi-file deliverables.
  • Friction: silent failure. The user has no idea anything was lost. The agent's "I've prepared the guides folder for you" final message becomes a lie with no indication.

Suggested approach:

  1. Stage 1 — Surface (A): in src/untether/telegram/outbox_delivery.py (scan_outbox / deliver_outbox), keep the existing outbox.skipped log line but also accumulate skipped items into a return value used by runner_bridge when composing the final message. Append a **📎 skipped:** line listing each (name, reason) pair. Add an [transports.telegram.files] config flag notify_skipped_outbox = true (default true) so users can opt out.
  2. Stage 2 — Optional zip (B): only after (A) ships. Add outbox_zip_directories = false config flag (opt-in initially), use zipfile from stdlib, respect deny-globs while zipping, cap at outbox_max_archive_bytes (default 50 MiB), fall back to (A) on cap exceed.
  3. Update docs/reference/transports/telegram.md outbox section.
  4. Tests in tests/test_outbox_delivery.py (does this exist? tests/test_telegram_files.py covers helpers; outbox_delivery may not have a dedicated test file yet).

Related code/files:

  • src/untether/telegram/outbox_delivery.py (scan_outbox, deliver_outbox, the outbox.skipped log site)
  • src/untether/telegram/files.py (deduplication / deny-glob primitives — would be reused for zip variant)
  • src/untether/runner_bridge.py (final message composition)
  • docs/reference/transports/telegram.md (outbox docs)
  • untether.toml [transports.telegram.files] section
  • tests/test_telegram_files.py (17 tests, file helpers)

Related issues / prior conversations:

Effort estimate: S for (A) alone; M for (A)+(B).

Milestone: v0.35.4 (reasoning: small surfacing change with clear user benefit; B can defer).

— filed by untether-staging monitor loop, pass 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto:monitor-auditAuto-filed by /monitor command audit loop (bugs + enhancements, perf and otherwise)bugSomething isn't workingseverity:majorSignificant functionality broken or seriously degraded; workaround exists but painful

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions