[v0.1/Config Review] font.size_px が TOML int を拒否し opacity/mouse の clamp が無警告 #143
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis#143
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
背景
config.tomlは 1.0 で frozen label が付くが、現行の型と validation に「今しか直せない」互換性コストが残っている。1.
font.size_px = 14が parse error になるEffectiveConfig.font.size_px: Option<f32>はtoml = "1.0"のデフォルト Deserialize を使う。TOML は int と float を区別するためsize_px = 14はinvalid type: integer, expected floatでDiagnosticKind::ParseError になる。docs と code がこれを仕様として宣言しているのが問題:
crates/felis-client-core/src/config.rs:500コメント:Must be a TOML float (size_px = 14.0, not 14)docs/reference/config.mdComplete example:size_px = 14.0のみ記載nix/hm-module.nixのsettingsexample も14.0現実に
14と書くユーザは多く、alacritty/kitty/wezterm は int/float どちらも受ける。1.0 後に「int も受け入れる」にすると既存の error ファイルが突然 valid になるのは breaking ではないが、「一度 error だった書式が通る」より「最初から int を受け入れる」方がクリーン。1.0 で frozen 前に直すチャンス。2.
window.opacity/mouse.scroll_multiplierの clamp が無警告validate.rs::runはfont_size,fallback_chain,palette,keymap,shaderのみ検証する。WindowConfig::clamped_opacity()(0.0..=1.0, NaN→1.0) とMouseConfig::clamped_scroll_multiplier()(0.1..=100.0) はvalidate.rsで warn せず、使用時に clamp するのみ。結果:
window.opacity = 2.5→1.0に clamp されるがfelis config checkは warn しない。reference/config.mdBehavior 表は「Clamped to[0.0, 1.0]at load」と書くが実装は load 時ではなく use 時。mouse.scroll_multiplier = -1→0.1に反転防止 clamp されるが同様に silent。window.backdrop = "blur"の OS 非対応はwindowstage の notice のみでconfig checkは通る(意図通りだが、docs の表に「load vs window stage」の区別がopacity/scroll_multiplierと整合しない)。font.size_pxがClampedを warn するのと同じ粒度で他 clamp も warn すべき。3.
theme.palette.indexedが string-key のままPaletteConfig.indexed: BTreeMap<String,String>は TOML の key が文字列であるための回避だが、JSON Schema ではBTreeMap<String,String>のまま出力され、数値16が文字列"16"として扱われる。proto のGridPaletteColor.index: uint32と語彙がずれる。TOML でも
indexed."16" = "#..."とindexed.16 = "#..."の両方が同じ文字列 key になるため、整数キーに見えて文字列という二重性が editor 補完で紛らわしい。問い
1.0 で以下を決める:
font.size_pxに int/float 両対応の custom deserializer を当て、14と14.0を同値として受けるか。schemarsのJsonSchemaもnumberとして両方受ける形にする。window.opacity/mouse.scroll_multiplierの範囲外をvalidate.rsでDiagnosticKind::ValueWarning にし、felis config checkで検出可能にするか。window.backdropの OS 非対応はconfig checkの scope 外として明記するか。theme.palette.indexedをBTreeMap<u8,String>+ custom TOML deserializer (string/int 両対応) に正規化するか、現行のStringキーを frozen として docs に理由を残すか。提案
font.size_pxはdeserialize_with = "deserialize_flexible_f32"を追加し、TOML int (64) と float (f64) の両方をf32に正規化。reference/config.mdの「Must be a TOML float」記述を削除し、example に14と14.0両方が valid と追記。nix/hm-module.nixの example は14.0のままでも int が通ることを担保。validate.rsにwindow_opacity/mouse_scroll_multiplier/window_backdropの warning パスを追加し、reference/config.mdBehavior 表の Stage 列をload(warn) に揃える。backdropの OS 非対応はwindowstage のまま残し、表に注記。theme.palette.indexedは 1.0 でStringのまま凍結するならreference/config.mdとfelis-config.schema.jsonのトップコメントに「TOML key は文字列であるためindexedはStringキー」理由を frozen として記録。u8に変えるならBREAKING.mdにbase: <sha>を追加しjust schemaを再生成。どちらかに 1.0 で一意に決める。判定基準
size_px = 14とsize_px = 14.0がfelis config checkで共に valid かつ同値としてshow-effectiveに現れること。window.opacity = 2/mouse.scroll_multiplier = -1がfelis config checkで Warning になること(現行の silent clamp でない)。reference/config.md「Behavior on missing / malformed values」表とvalidate.rsの warning 対象が 1:1 で対応すること。felis-config.schema.jsonのfont.size_pxがtype: numberで int/float 両方を受けること。対象ファイル
crates/felis-client-core/src/config.rs(FontConfig.size_px,WindowConfig.opacity,MouseConfig.scroll_multiplier,PaletteConfig.indexed)crates/felis-client-core/src/config/validate.rs(run,font_sizeに倣った新関数)crates/felis-client-core/felis-config.schema.json(just schema)docs/reference/config.md「Complete annotated example」「Behavior on missing / malformed values」nix/hm-module.nix(settings.example)Parent: #12 および #11 / #28
Triage (2026-09-05)
Verdict: accept with narrowing, P1. Verified:
config.rs:499documents "Must be a TOML float" as behavior.size_px = 14): do it now. Accept TOML int and float, drop the "must be a float" wording, keep the schema atnumber.palette.indexedkeys): freeze asStringand record the reason (TOML keys are strings) inreference/config.mdand the schema header. Changing the key type buys nothing the wire does not already own.opacity/scroll_multiplierclamps, theloadvsusestage wording): additive, no now-or-never property, so it lands after v0.1.0 unless it falls out of item 1 for free. Fix the Behavior table's stage column to match the code either way, since that table is frozen prose.Triage plan (2026-09-05)
Source-grounded triage against
mainat07a77ae4, reviewed through six rounds of an independent reviewer (piluna 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).font.size_pxint, clamp warnings,palette.indexedkeysClaim check
toml 1.1.5,Option<f32>deserializessize_px = 14toSome(14.0)(checked witha standalone program against the same crate version). The "Must be a
TOML float" sentence (
config.rs:499-500) documents a restriction thatdoes not exist; the docs example and the HM example are merely
float-only, not wrong.
validate.rs:57-150coversfont_size,fallback_chain,palette,keymap,shader;clamped_opacity(
config.rs:479) andclamped_scroll_multiplier(:296) clamp at usewith no diagnostic, while
docs/reference/config.md:238says"
window.opacityout of range → Clamped … load".PaletteConfig.indexed: BTreeMap<String, String>(
config.rs:683), parsed byindexed_slot(:718).Verdict
accept with narrowing.
14and14.0are equal.Stringkeys; record the reason (TOML keys are strings;indexed.16andindexed."16"are one key) inreference/config.mdand the schema header.
are two ~15-line functions following
font_size, and the Behavior tablerow is wrong today and gets edited in this PR either way; shipping the
table as "clamped at use, silently" freezes prose that describes a gap.
Alternative (triage): fix the table wording to "window/use" and defer the
warnings post-release.
Approach
config.rs:499-500wording;validate.rs:window_opacity,mouse_scroll_multiplier→DiagnosticKind::Valuewarning when outsidethe clamp band or non-finite;
window.backdropstays a window-stagenotice, said so in the table.
clamped_scroll_multiplier(config.rs:293-298) isf64::clamp, whichpasses NaN through, and
felis-client/src/main.rs:1768-1779multiplieswheel deltas by it, so
scroll_multiplier = nanzeroes every notch.Give it the same non-finite fallback
clamped_opacityhas (the defaultmultiplier) and test the sanitized value, not only the warning.
with
config.rs:nix/hm-module.nix:116-117,docs/how-to/install.md:62,.claude/skills/add-config-key/SKILL.md:82, and the reference example.docs/reference/config.md: example showssize_px = 14accepted;Behavior table:
opacityrow → "Clamped to[0.0, 1.0]with warning,load"; add the
scroll_multiplierrow; note under the table that thevaluediagnostics are 1:1 withvalidate.rs.size_px(numberalready admits both); headergains the
indexedsentence;just schema.CHANGELOG.md: the new warnings (user-visible inconfig check).values produce no diagnostic).
Dependencies / risk / labels
Before #29 (schema header). S. Keep labels.