问题描述
一句话:macOS 上 os.UserConfigDir() 返回 ~/Library/Application Support/,但文档和渲染注释全部硬编码为 ~/.config/reasonix/config.toml,导致用户把配置放在 ~/.config/reasonix/ 后完全不生效。
并且沙箱 bash = "enforce" 阻止了从内部修复——形成鸡生蛋死锁。
根因
代码 internal/config/config.go 中:
func userConfigPath() string {
dir, err := os.UserConfigDir() // macOS → ~/Library/Application Support
...
return filepath.Join(dir, "reasonix", "config.toml")
}
macOS 上 os.UserConfigDir() 返回 ~/Library/Application Support/,而非 ~/.config/。参见 Go 标准库文档。
连锁反应
1. 文档全部错误
所有以下位置硬编码了 ~/.config/reasonix/config.toml,macOS 上均为错误信息:
internal/config/config.go:2 — 注释
internal/config/render.go:43 — 渲染到用户配置文件顶部的注释
internal/config/config.go:1543 — UserConfigPath 文档注释
internal/config/config.go:1548 — UserCredentialsPath 文档注释
README.md / README.zh-CN.md / docs/MIGRATING.md / docs/SPEC.md
渲染出的 config.toml 顶部注释:
# Resolution order: flag > ./reasonix.toml > ~/.config/reasonix/config.toml > built-in defaults.
macOS 用户看到这个会以为配置放 ~/.config/reasonix/ 就行,但实际上放那里完全不会被读取。
2. 沙箱鸡生蛋死锁
- 用户把配置放到
~/.config/reasonix/config.toml(文档说的路径)—— reasonix 不读
- reasonix 实际读的是
~/Library/Application Support/reasonix/config.toml——但这个文件是自动生成的,没有 allow_write
- 没有
allow_write → 沙箱阻止写入任何非 workspace 路径
- 无法写入 → 无法添加
allow_write 到正确的配置文件
结果:用户根本无法通过自身工具修复沙箱写入权限。
3. 用户实际状态
macOS 上最终出现两个并行目录:
~/.config/reasonix/ ← 用户按文档放的,reasonix 不读
├── config.toml ← 手动写的(有 allow_write ✅)
├── REASONIX.md ← 全局指令 ✅
└── credentials ← 密钥 ✅
~/Library/Application Support/reasonix/ ← reasonix 实际读取
├── config.toml ← 自动生成(无 allow_write ❌)
├── credentials ← 密钥 ✅(可能和上面重复)
├── cache/
├── sessions/
└── projects/
两个 credentials 文件可能不同步,config.toml 则完全不一致。
4. 跨项目覆盖问题
即使 ~/Library/Application Support/reasonix/config.toml 里写了 allow_write,如果某个项目的 reasonix.toml 有自己的 [sandbox] 段但没有 allow_write,它仍然会覆盖全局配置——因为加载顺序是后加载的项目配置覆盖前面的用户配置。
复现步骤
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
cfgDir, _ := os.UserConfigDir()
fmt.Println("os.UserConfigDir():", cfgDir)
fmt.Println("Reasonix user config path used by code:",
filepath.Join(cfgDir, "reasonix", "config.toml"))
fmt.Println("Reasonix user config path per docs:",
"~/.config/reasonix/config.toml")
}
macOS 输出:
os.UserConfigDir(): /Users/sven/Library/Application Support
Reasonix user config path used by code: /Users/sven/Library/Application Support/reasonix/config.toml
Reasonix user config path per docs: ~/.config/reasonix/config.toml
修复建议
方案 A(推荐):添加 ~/.config/reasonix/ fallback
在 userConfigPath() 中,如果 os.UserConfigDir() 返回的不是 ~/.config/ 路径,额外检查 $HOME/.config/reasonix/config.toml 是否存在:
func userConfigPath() string {
dir, err := os.UserConfigDir()
if err != nil {
return ""
}
path := filepath.Join(dir, "reasonix", "config.toml")
// macOS: os.UserConfigDir() returns ~/Library/Application Support
// but users may have configured ~/.config/reasonix/ per the docs.
// Check for it as a fallback so documented path actually works.
if _, err := os.Stat(path); os.IsNotExist(err) {
if home, _ := os.UserHomeDir(); home != "" {
alt := filepath.Join(home, ".config", "reasonix", "config.toml")
if _, err := os.Stat(alt); err == nil {
return alt
}
}
}
return path
}
同样逻辑应该应用到 UserCredentialsPath()、UserConfigPath()、SessionDirForRoot()、ProjectSessionDir() 等所有使用 os.UserConfigDir() 拼接路径的函数。
方案 B:只修文档
把所有硬编码的 ~/.config/reasonix/ 改为描述性语言,缺点是 macOS 用户仍然需要自己去猜正确的路径。
方案 C:支持 XDG_CONFIG_HOME 环境变量
让 XDG_CONFIG_HOME 环境变量优先于 os.UserConfigDir(),这样用户可以显式指定。
受影响的文件
internal/config/config.go — userConfigPath(), UserConfigPath(), UserCredentialsPath(), 注释
internal/config/render.go:43 — 渲染注释
README.md, README.zh-CN.md, docs/MIGRATING.md, docs/SPEC.md — 文档
问题描述
一句话:macOS 上
os.UserConfigDir()返回~/Library/Application Support/,但文档和渲染注释全部硬编码为~/.config/reasonix/config.toml,导致用户把配置放在~/.config/reasonix/后完全不生效。并且沙箱
bash = "enforce"阻止了从内部修复——形成鸡生蛋死锁。根因
代码
internal/config/config.go中:macOS 上
os.UserConfigDir()返回~/Library/Application Support/,而非~/.config/。参见 Go 标准库文档。连锁反应
1. 文档全部错误
所有以下位置硬编码了
~/.config/reasonix/config.toml,macOS 上均为错误信息:internal/config/config.go:2— 注释internal/config/render.go:43— 渲染到用户配置文件顶部的注释internal/config/config.go:1543—UserConfigPath文档注释internal/config/config.go:1548—UserCredentialsPath文档注释README.md/README.zh-CN.md/docs/MIGRATING.md/docs/SPEC.md渲染出的
config.toml顶部注释:macOS 用户看到这个会以为配置放
~/.config/reasonix/就行,但实际上放那里完全不会被读取。2. 沙箱鸡生蛋死锁
~/.config/reasonix/config.toml(文档说的路径)—— reasonix 不读~/Library/Application Support/reasonix/config.toml——但这个文件是自动生成的,没有allow_writeallow_write→ 沙箱阻止写入任何非 workspace 路径allow_write到正确的配置文件结果:用户根本无法通过自身工具修复沙箱写入权限。
3. 用户实际状态
macOS 上最终出现两个并行目录:
两个
credentials文件可能不同步,config.toml则完全不一致。4. 跨项目覆盖问题
即使
~/Library/Application Support/reasonix/config.toml里写了allow_write,如果某个项目的reasonix.toml有自己的[sandbox]段但没有allow_write,它仍然会覆盖全局配置——因为加载顺序是后加载的项目配置覆盖前面的用户配置。复现步骤
macOS 输出:
修复建议
方案 A(推荐):添加
~/.config/reasonix/fallback在
userConfigPath()中,如果os.UserConfigDir()返回的不是~/.config/路径,额外检查$HOME/.config/reasonix/config.toml是否存在:同样逻辑应该应用到
UserCredentialsPath()、UserConfigPath()、SessionDirForRoot()、ProjectSessionDir()等所有使用os.UserConfigDir()拼接路径的函数。方案 B:只修文档
把所有硬编码的
~/.config/reasonix/改为描述性语言,缺点是 macOS 用户仍然需要自己去猜正确的路径。方案 C:支持 XDG_CONFIG_HOME 环境变量
让
XDG_CONFIG_HOME环境变量优先于os.UserConfigDir(),这样用户可以显式指定。受影响的文件
internal/config/config.go—userConfigPath(),UserConfigPath(),UserCredentialsPath(), 注释internal/config/render.go:43— 渲染注释README.md,README.zh-CN.md,docs/MIGRATING.md,docs/SPEC.md— 文档