[v0.1/P1] Resolve pipe file targets relative to config.toml #335

Closed
opened 2026-09-21 23:17:30 +09:00 by natsukium · 1 comment
Owner

Problem

The config design states that file-valued keys resolve relative to config.toml, not the process working directory, because GUI launches commonly have an arbitrary cwd. The keymap pipe file sink is the exception: its string is carried unchanged into write_region_file, so a relative path resolves against the client process cwd.

This is both surprising and unstable:

  • the same durable config writes to different locations depending on how felis was launched;
  • it contradicts the rationale already applied to shader paths;
  • a session's cwd is not used either, so the current behavior does not provide a useful “relative to the focused shell” semantic.

Proposed resolution

Resolve target = { file = "..." } at config-load time using the same source-directory and ~/ rules as other file-valued config. Carry the resolved path in the runtime action rather than retaining a cwd-sensitive string.

If a config source has no directory, define one explicit fallback rather than inheriting whatever cwd the write happens to observe.

Command argv remains separate: executable/path resolution inside target = { command = [...] } follows normal client process execution rules and is not a file-valued config key.

Acceptance criteria

  • Relative pipe file targets resolve against the containing config file's directory.
  • ~/ and absolute paths behave consistently with other file-valued keys.
  • Reloading a config preserves the same resolution base.
  • A config source without a directory has one documented deterministic rule.
  • Tests launch/load from a different cwd and verify the destination path.
  • Config/keybinding reference and control-surface explanation state the same rule.
  • The config schema remains accurate.

Evidence

  • crates/felis-client-core/src/keymap/map.rs (PipeSink::File)
  • crates/felis-client-core/src/config.rs (resolve_path)
  • crates/felis-client/src/app_methods.rs (finish_pipe)
  • docs/reference/keybindings.md
  • docs/explanation/architecture/control-surfaces.md

Related: #8, #326.

## Problem The config design states that file-valued keys resolve relative to `config.toml`, not the process working directory, because GUI launches commonly have an arbitrary cwd. The keymap `pipe` file sink is the exception: its string is carried unchanged into `write_region_file`, so a relative path resolves against the client process cwd. This is both surprising and unstable: - the same durable config writes to different locations depending on how felis was launched; - it contradicts the rationale already applied to shader paths; - a session's cwd is not used either, so the current behavior does not provide a useful “relative to the focused shell” semantic. ## Proposed resolution Resolve `target = { file = "..." }` at config-load time using the same source-directory and `~/` rules as other file-valued config. Carry the resolved path in the runtime action rather than retaining a cwd-sensitive string. If a config source has no directory, define one explicit fallback rather than inheriting whatever cwd the write happens to observe. Command argv remains separate: executable/path resolution inside `target = { command = [...] }` follows normal client process execution rules and is not a file-valued config key. ## Acceptance criteria - [ ] Relative pipe file targets resolve against the containing config file's directory. - [ ] `~/` and absolute paths behave consistently with other file-valued keys. - [ ] Reloading a config preserves the same resolution base. - [ ] A config source without a directory has one documented deterministic rule. - [ ] Tests launch/load from a different cwd and verify the destination path. - [ ] Config/keybinding reference and control-surface explanation state the same rule. - [ ] The config schema remains accurate. ## Evidence - `crates/felis-client-core/src/keymap/map.rs` (`PipeSink::File`) - `crates/felis-client-core/src/config.rs` (`resolve_path`) - `crates/felis-client/src/app_methods.rs` (`finish_pipe`) - `docs/reference/keybindings.md` - `docs/explanation/architecture/control-surfaces.md` Related: #8, #326.
Author
Owner

Decision: do, as a documented behavior change aligned to the recorded rule (reviewed)

PipeSink::File is carried unchanged into PipeTarget::File(Some(path)) and reaches write_region_file without passing resolve_path. control-surfaces.md records "file-valued keys resolve against config.toml, not the process's working directory", but keybindings.md currently documents the pipe path as verbatim, cwd-relative, no ~/, so the reference contradicts the explanation and this is a CHANGELOG.md entry, not a silent fix.

Resolution: resolve at config load with the same resolve_path(name, base_dir) that shader.post uses, so ~/ and absolute paths behave like every other file-valued key. No new fallback is invented: every production document is read from a path and has a parent, missing discovery yields EffectiveConfig::default() with no file key at all, and only a programmatic parse-from-text has source_dir = None, where relative paths resolve against the working directory as EffectiveConfig::source_dir already documents. (There is no --config - or embedded source.) Tests load from a different cwd and check the destination; keybindings.md and the control-surfaces explanation are updated together.

## Decision: do, as a documented behavior change aligned to the recorded rule (reviewed) `PipeSink::File` is carried unchanged into `PipeTarget::File(Some(path))` and reaches `write_region_file` without passing `resolve_path`. `control-surfaces.md` records "file-valued keys resolve against `config.toml`, not the process's working directory", but `keybindings.md` currently documents the pipe path as verbatim, cwd-relative, no `~/`, so the reference contradicts the explanation and this is a `CHANGELOG.md` entry, not a silent fix. Resolution: resolve at config load with the same `resolve_path(name, base_dir)` that `shader.post` uses, so `~/` and absolute paths behave like every other file-valued key. No new fallback is invented: every production document is read from a path and has a parent, missing discovery yields `EffectiveConfig::default()` with no file key at all, and only a programmatic parse-from-text has `source_dir = None`, where relative paths resolve against the working directory as `EffectiveConfig::source_dir` already documents. (There is no `--config -` or embedded source.) Tests load from a different cwd and check the destination; `keybindings.md` and the control-surfaces explanation are updated together.
Commenting is not possible because the repository is archived.
No description provided.