[v0.1/Config Review] Windows/macOS の config 探索パス二重化は 1.0 で正規化すべき #137

Closed
opened 2026-09-05 11:37:45 +09:00 by natsukium · 3 comments
Owner

背景

config.toml の探索パスは directories::ProjectDirs に委譲され、1.0 で凍結すると破壊的変更が不可能になる。

  • Linux: $XDG_CONFIG_HOME/felis/config.toml (~/.config/felis/config.toml) — 妥当。
  • macOS: ~/Library/Application Support/felis/config.toml 固定で ~/.config/felis/ は無視 — CLI ツールとしては XDG_CONFIG_HOME を優先するのが一般的(flake.nix の devShell も XDG を置く)。現行は docs/how-to/fix-terminfo-problems.md で混乱が報告されている。
  • Windows: %APPDATA%\felis\config\config.tomlProjectDirs::config_dir() が返す %APPDATA%\felis\config\config.toml を足した結果、config セグメントが二重。期待される %APPDATA%\felis\config.toml ではない。以後ユーザが手で作ったファイルは config\config.toml に固定される。Home Manager の symlink 管理 (config.toml -> /nix/store/...) でも mtime ではなく resolved path の変化で reload する特殊分岐が入っている。

felis config の E2E テストが Windows で cfg(unix) に限定されているのも、--config <path> / FELIS_CONFIG の override が無いため実 profile を置換せずテストできないことが直接原因。

これらは filesystem 上の「どこに置くか」という契約で、1.0 で frozen label を貼った後は移行コストが高い(既存ファイルを移動する migration / deprecation warn が必要)。互換性を無視できる今しか直せない。

問い

1.0 で以下を決める:

  1. Windows パスを flatten するか%APPDATA%\felis\config.toml に変更し、二重 config\ を解消する。ProjectDirs をやめて dirs / 手書き解決に置き換えるか、ProjectDirs の結果から末尾 config を strip するか。
  2. macOS で XDG をフォールバックにするか~/Library/Application Support/felis/config.toml を第一候補、$XDG_CONFIG_HOME/felis/config.toml~/.config/felis/config.toml を第二候補として両方読む、または XDG を優先する。reference/config.md の Platform 表を「どちらが選ばれるか」で一意に決める。
  3. 相対 --config の解決ベースを freeze するか — 現行は felis 起動時の cwd に対して絶対化し frontend に転送する(resolve_config_source)。FELIS_CONFIG env を持たない理由(spawning env への漏洩)は explanation/architecture/control-surfaces.md にあるが、Windows の drive-relative C:foo.toml を拒否する分岐は 1.0 で公開契約として残すか。

提案

  • Windows パスは 1.0 で %APPDATA%\felis\config.toml に一本化(breaking だが今しかできない)。explanation/architecture/control-surfaces.md に「なぜ ProjectDirs のデフォルトを上書きしたか」を記録し、--config の存在を移行手段として明記。
  • macOS は XDG_CONFIG_HOME が設定されていればそれを優先、なければ ~/Library/Application Support/felis/config.toml、さらにフォールバックとして ~/.config/felis/config.toml を読む順序で凍結。felis config path が実際に読む候補を列挙して返すようにし、テストは --config で代替できていることを担保。
  • これらを reference/config.md「Platform file locations」表に frozen として刻み、just schemanix/hm-module.nix の expected path を同時に更新。CHANGELOG.md Unreleased に migration note。

判定基準

  • 新規ユーザが felis config path の出力だけで「どこに何を書けばいいか」迷わないこと。
  • 既存 config\config.toml を置いたユーザが 1.0 で黙ってデフォルトに戻らないこと(migration warn か両パスを読む猶予を 1 minor 置くか、いずれかを docs に明記)。
  • Windows の E2E が cfg(unix) 限定でなく --config 経由で通ること。
  • reference/config.md / explanation/architecture/control-surfaces.md / how-to/install / nix/hm-module.nix / skills/felis が同一 path を示すこと(doc-cascade の grep sweep)。

対象ファイル

  • crates/felis-client-core/src/config.rs (config_path, ConfigSource::resolve)
  • crates/felis-client-core/felis-config.schema.json / nix/hm-module.nix
  • docs/reference/config.md 「Platform file locations」
  • docs/explanation/architecture/control-surfaces.md 「Lenient config / Platform」
  • docs/how-to/* / flake.nix (devShell の XDG)

Parent: #12 および #11 / #4, #27 (本 issue は #4 の「探索パスと platform 差」の破壊的決定を具体化、--config 自体は #27)

## 背景 `config.toml` の探索パスは `directories::ProjectDirs` に委譲され、1.0 で凍結すると破壊的変更が不可能になる。 - Linux: `$XDG_CONFIG_HOME/felis/config.toml` (`~/.config/felis/config.toml`) — 妥当。 - macOS: `~/Library/Application Support/felis/config.toml` 固定で `~/.config/felis/` は無視 — CLI ツールとしては `XDG_CONFIG_HOME` を優先するのが一般的(`flake.nix` の devShell も XDG を置く)。現行は `docs/how-to/fix-terminfo-problems.md` で混乱が報告されている。 - Windows: `%APPDATA%\felis\config\config.toml` — `ProjectDirs::config_dir()` が返す `%APPDATA%\felis\config\` に `config.toml` を足した結果、`config` セグメントが二重。期待される `%APPDATA%\felis\config.toml` ではない。以後ユーザが手で作ったファイルは `config\config.toml` に固定される。Home Manager の symlink 管理 (`config.toml -> /nix/store/...`) でも mtime ではなく resolved path の変化で reload する特殊分岐が入っている。 `felis config` の E2E テストが Windows で `cfg(unix)` に限定されているのも、`--config <path>` / `FELIS_CONFIG` の override が無いため実 profile を置換せずテストできないことが直接原因。 これらは filesystem 上の「どこに置くか」という契約で、1.0 で frozen label を貼った後は移行コストが高い(既存ファイルを移動する migration / deprecation warn が必要)。互換性を無視できる今しか直せない。 ## 問い 1.0 で以下を決める: 1. **Windows パスを flatten するか** — `%APPDATA%\felis\config.toml` に変更し、二重 `config\` を解消する。`ProjectDirs` をやめて `dirs` / 手書き解決に置き換えるか、`ProjectDirs` の結果から末尾 `config` を strip するか。 2. **macOS で XDG をフォールバックにするか** — `~/Library/Application Support/felis/config.toml` を第一候補、`$XDG_CONFIG_HOME/felis/config.toml` → `~/.config/felis/config.toml` を第二候補として両方読む、または XDG を優先する。`reference/config.md` の Platform 表を「どちらが選ばれるか」で一意に決める。 3. **相対 `--config` の解決ベースを freeze するか** — 現行は `felis` 起動時の cwd に対して絶対化し frontend に転送する(`resolve_config_source`)。`FELIS_CONFIG` env を持たない理由(spawning env への漏洩)は `explanation/architecture/control-surfaces.md` にあるが、Windows の drive-relative `C:foo.toml` を拒否する分岐は 1.0 で公開契約として残すか。 ## 提案 - Windows パスは 1.0 で `%APPDATA%\felis\config.toml` に一本化(breaking だが今しかできない)。`explanation/architecture/control-surfaces.md` に「なぜ ProjectDirs のデフォルトを上書きしたか」を記録し、`--config` の存在を移行手段として明記。 - macOS は `XDG_CONFIG_HOME` が設定されていればそれを優先、なければ `~/Library/Application Support/felis/config.toml`、さらにフォールバックとして `~/.config/felis/config.toml` を読む順序で凍結。`felis config path` が実際に読む候補を列挙して返すようにし、テストは `--config` で代替できていることを担保。 - これらを `reference/config.md`「Platform file locations」表に frozen として刻み、`just schema` と `nix/hm-module.nix` の expected path を同時に更新。`CHANGELOG.md` Unreleased に migration note。 ## 判定基準 - 新規ユーザが `felis config path` の出力だけで「どこに何を書けばいいか」迷わないこと。 - 既存 `config\config.toml` を置いたユーザが 1.0 で黙ってデフォルトに戻らないこと(migration warn か両パスを読む猶予を 1 minor 置くか、いずれかを docs に明記)。 - Windows の E2E が `cfg(unix)` 限定でなく `--config` 経由で通ること。 - `reference/config.md` / `explanation/architecture/control-surfaces.md` / `how-to/install` / `nix/hm-module.nix` / `skills/felis` が同一 path を示すこと(`doc-cascade` の grep sweep)。 ## 対象ファイル - `crates/felis-client-core/src/config.rs` (`config_path`, `ConfigSource::resolve`) - `crates/felis-client-core/felis-config.schema.json` / `nix/hm-module.nix` - `docs/reference/config.md` 「Platform file locations」 - `docs/explanation/architecture/control-surfaces.md` 「Lenient config / Platform」 - `docs/how-to/*` / `flake.nix` (devShell の XDG) Parent: #12 および #11 / #4, #27 (本 issue は #4 の「探索パスと platform 差」の破壊的決定を具体化、`--config` 自体は #27)
Author
Owner

Triage (2026-09-05)

Verdict: accept with narrowing, P1. P1 only because reference/config.md documents the paths and that table freezes; v0.1.0 advertises x86_64-linux only, so the platforms themselves are ungated.

  1. Windows: flatten to %APPDATA%\felis\config.toml. The doubled config\ is a ProjectDirs artifact that the docs currently reproduce as if intended. No migration shim: nothing has been released.
  2. macOS: keep the single location ~/Library/Application Support/felis/config.toml. An XDG fallback introduces a precedence rule and a config path output with two candidates for a platform v0.1.0 does not gate; --config already covers the "I keep dotfiles in XDG" case explicitly. Record the rejection and a Revisit trigger in explanation/architecture/control-surfaces.md.
  3. Relative --config resolution is #27's recorded decision; out of scope here.

Cascade: reference/config.md Platform table, nix/hm-module.nix, skills/felis, CHANGELOG.md.

## Triage (2026-09-05) **Verdict: accept with narrowing, P1.** P1 only because `reference/config.md` documents the paths and that table freezes; v0.1.0 advertises x86_64-linux only, so the platforms themselves are ungated. 1. Windows: flatten to `%APPDATA%\felis\config.toml`. The doubled `config\` is a `ProjectDirs` artifact that the docs currently reproduce as if intended. No migration shim: nothing has been released. 2. macOS: keep the single location `~/Library/Application Support/felis/config.toml`. An XDG fallback introduces a precedence rule and a `config path` output with two candidates for a platform v0.1.0 does not gate; `--config` already covers the "I keep dotfiles in XDG" case explicitly. Record the rejection and a Revisit trigger in `explanation/architecture/control-surfaces.md`. 3. Relative `--config` resolution is #27's recorded decision; out of scope here. Cascade: `reference/config.md` Platform table, `nix/hm-module.nix`, `skills/felis`, `CHANGELOG.md`.
Author
Owner

directories::ProjectDirs に従う、で良いと思う

`directories::ProjectDirs` に従う、で良いと思う
Author
Owner

Triage plan (2026-09-05)

Source-grounded triage against main at 07a77ae4, reviewed through six rounds of an independent reviewer (pi luna then sol); every finding was verified against the source and folded in, and the design decisions below were settled with the maintainer on 2026-09-05. The order that supersedes the tracker's is posted on #12: this issue lands before #29 (schemas, goldens, and reference tables freeze there).

Config discovery paths: keep directories::ProjectDirs, freeze the table

Claim check

  • crates/felis-client-core/src/config/document.rs:408-417: the path is
    ProjectDirs::from("", "", "felis").config_dir().join("config.toml"),
    which yields %APPDATA%\felis\config\config.toml on Windows and
    ~/Library/Application Support/felis/config.toml on macOS. The
    reference table (docs/reference/config.md:15-22), nix/hm-module.nix:107-108,192-203,
    and the how-to already state these paths consistently.
  • The Windows cfg(unix) test-gating argument is moot since #27: --config
    exists precisely so the Windows config and doctor suites can isolate a
    temp profile (docs/explanation/architecture/control-surfaces.md:222-229).

Verdict

accept, docs-only. Maintainer decision on the issue (2026-09-05):
follow directories::ProjectDirs as-is. No path changes on any platform.

Approach

  • docs/explanation/architecture/control-surfaces.md "Lenient config /
    Platform": record the decision inline. Rationale: one resolver owned by
    a maintained crate instead of a felis-owned per-platform table; the
    doubled config\ on Windows and the non-XDG macOS location are that
    crate's conventions, and --config is the escape hatch for a user who
    keeps dotfiles elsewhere. Rejected: flattening Windows (a felis-specific
    override of the resolver that every future ProjectDirs consumer in felis
    would have to mirror), XDG fallback on macOS (a two-candidate config path output and a precedence rule for a platform v0.1.0 does not gate).
    Revisit if: ProjectDirs changes its layout, or macOS becomes a gated
    platform and users report the location as a support load.
  • docs/reference/config.md Platform table: mark frozen for 1.0; keep the
    existing two sentences of guidance.
  • The three #[cfg(unix)] tests in crates/felis-cli/tests/cli_config.rs
    each have their own reason, so handle them one by one:
    default_discovery_is_untouched_when_the_flag_is_absent (:72) tests
    discovery itself and stays Unix (on Windows ProjectDirs reads the
    known-folder API, not APPDATA, so a child cannot be redirected — the
    test's own comment); it gains a Windows-only twin that runs config path --format json without reading the file and asserts the reported
    path ends in felis\config\config.toml and starts with the roaming
    app-data folder — the executable form of the frozen table row; an_unreadable_selected_file_reports_io
    (:127) uses PermissionsExt, so make it portable by selecting a
    directory as the config path instead; the relative-path test (:175)
    needs Windows path spelling in its assertion, then un-gates.
    cli_version.rs / cli_daemon.rs are whole-file #![cfg(unix)] for
    socket reasons and stay.
  • Cascade sweep: nix/hm-module.nix, skills/felis, docs/how-to/* for
    the three paths (they agree today; the sweep is the proof).

Dependencies / risk / labels

None. S. Keep labels. Not a CHANGELOG.md entry (nothing changes).

## Triage plan (2026-09-05) Source-grounded triage against `main` at `07a77ae4`, reviewed through six rounds of an independent reviewer (`pi` luna then sol); every finding was verified against the source and folded in, and the design decisions below were settled with the maintainer on 2026-09-05. The order that supersedes the tracker's is posted on #12: this issue lands before #29 (schemas, goldens, and reference tables freeze there). **Config discovery paths: keep `directories::ProjectDirs`, freeze the table** ### Claim check - `crates/felis-client-core/src/config/document.rs:408-417`: the path is `ProjectDirs::from("", "", "felis").config_dir().join("config.toml")`, which yields `%APPDATA%\felis\config\config.toml` on Windows and `~/Library/Application Support/felis/config.toml` on macOS. The reference table (`docs/reference/config.md:15-22`), `nix/hm-module.nix:107-108,192-203`, and the how-to already state these paths consistently. - The Windows `cfg(unix)` test-gating argument is moot since #27: `--config` exists precisely so the Windows config and doctor suites can isolate a temp profile (`docs/explanation/architecture/control-surfaces.md:222-229`). ### Verdict **accept, docs-only.** Maintainer decision on the issue (2026-09-05): follow `directories::ProjectDirs` as-is. No path changes on any platform. ### Approach - `docs/explanation/architecture/control-surfaces.md` "Lenient config / Platform": record the decision inline. Rationale: one resolver owned by a maintained crate instead of a felis-owned per-platform table; the doubled `config\` on Windows and the non-XDG macOS location are that crate's conventions, and `--config` is the escape hatch for a user who keeps dotfiles elsewhere. Rejected: flattening Windows (a felis-specific override of the resolver that every future ProjectDirs consumer in felis would have to mirror), XDG fallback on macOS (a two-candidate `config path` output and a precedence rule for a platform v0.1.0 does not gate). Revisit if: ProjectDirs changes its layout, or macOS becomes a gated platform and users report the location as a support load. - `docs/reference/config.md` Platform table: mark frozen for 1.0; keep the existing two sentences of guidance. - The three `#[cfg(unix)]` tests in `crates/felis-cli/tests/cli_config.rs` each have their own reason, so handle them one by one: `default_discovery_is_untouched_when_the_flag_is_absent` (`:72`) tests discovery itself and stays Unix (on Windows `ProjectDirs` reads the known-folder API, not `APPDATA`, so a child cannot be redirected — the test's own comment); it gains a Windows-only twin that runs `config path --format json` without reading the file and asserts the reported path ends in `felis\config\config.toml` and starts with the roaming app-data folder — the executable form of the frozen table row; `an_unreadable_selected_file_reports_io` (`:127`) uses `PermissionsExt`, so make it portable by selecting a directory as the config path instead; the relative-path test (`:175`) needs Windows path spelling in its assertion, then un-gates. `cli_version.rs` / `cli_daemon.rs` are whole-file `#![cfg(unix)]` for socket reasons and stay. - Cascade sweep: `nix/hm-module.nix`, `skills/felis`, `docs/how-to/*` for the three paths (they agree today; the sweep is the proof). ### Dependencies / risk / labels None. **S.** Keep labels. Not a `CHANGELOG.md` entry (nothing changes).
Sign in to join this conversation.
No description provided.