SOW-0172: Rust log writer accepts relative directories

KT
10h ago
+680-186

Stacked on #22. Completes SOW-0172 (.agents/sow/done/SOW-0172-20261103-rust-log-relative-directory.md).

Decisions

  • User decision: option A. Rust Log makes the directory absolute once, at construction, without following symlinks.

Problem

  • Rust Log::new(".local/x", ...) created <machine-id>/ and then failed on the first write with failed to create journal file. journal-registry File::from_path rejects relative paths, and create_chain discarded its canonicalize() result.
  • In root-retention mode, pruning skipped every file under a relative root without any error.
  • An empty directory created <machine-id>/ in the working directory before failing. Go refuses an empty directory up front.

Changes

  • Log::new_inner resolves the directory with std::path::absolute and returns InvalidPath on failure, which includes the empty path.
  • Rust path accessors (configured_directory, journal_directory, active_path) now return absolute paths. Go keeps the path as given. This is documented in docs/Writer-APIs.md, product-scope.md and the rustdoc.
  • Tests:
    • Rust: relative writer (write, rotate, reopen), relative root pruning, and empty-path refusal. All three fail without the fix.
    • Go: matching relative-directory and empty-directory tests.
  • New pending SOW-0173: Rust Registry::watch_directory finds no files under a relative directory. It needs a decision.

Validation

  • go vet ./journal and go test ./... pass.
  • cargo test --workspace passes.
  • Clippy reports nothing in the changed files. Older lints elsewhere predate this change.
  • check_wiki_docs.py: OK. SOW audit: clean.

Summary by cubic

Makes the Rust Log writer accept relative directories like Go does: the directory is resolved to absolute once at construction, so writes, rotation, reopening, and root-retention pruning work, and an empty directory is refused upfront with InvalidPath instead of creating a stray <machine-id>/ directory before failing on the first write. Rust path accessors now return absolute paths while Go returns the path as given. The registry's related gap is tracked as pending SOW-0173: Registry::watch_directory silently finds no files under a relative directory.