Skip to content

Project-scope instructions are overly denied — need glob + rules directory auto-discovery #3867

Description

@yekern

Summary

CodeWhale's instruction system is nearly unusable in multi-project workflows. The instructions config key has been hard-blocked at project scope since v0.8.8, with no trust awareness, no glob support, and no rules-directory auto-discovery — making it impossible to maintain per-project .claude/rules/-style rule files without manually editing the global user config for every new file.

Symptoms

1. Project-scope instructions hard-ignored (regardless of trust)

# .codewhale/config.toml (project-level — silently ignored!)
trust_level = "trusted"
instructions = [".claude/rules/coding-style.md", ".claude/rules/naming.md"]

Users must list every rule file in ~/.codewhale/config.toml (global user scope), which is painful when maintaining multiple projects.

Relevant code: crates/tui/src/main.rs:6309-6315instructions is independently hard-coded as denied, treated identically to api_key hijacking.

2. No glob / wildcard support

# Does not work — "*.md" is treated as a literal filename
instructions = [".claude/rules/*.md"]

Relevant code: crates/tui/src/config.rs:3591-3601instructions_paths() only does expand_path (~ and env vars); no glob expansion.

3. No rules-directory auto-discovery

Claude Code auto-discovers all .md files under .claude/rules/. CodeWhale has no equivalent.

Relevant code: crates/tui/src/project_context.rs:34-41PROJECT_CONTEXT_FILES only lists fixed filenames (AGENTS.md, CLAUDE.md, etc.); does not scan .codewhale/rules/ or .claude/rules/.

4. No conditional rule loading (YAML frontmatter + paths matching)

Claude Code's rules support YAML frontmatter with a paths key that gates loading on whether the current workspace activity matches a glob pattern. Irrelevant rules are skipped — keeping the prompt lean and reducing "lost in the middle" noise.

---
paths: "src/**/*.rs"
description: "Rust coding conventions"
---

# Rust Rules
Use `const` for module-level constants, not `static`.

If no frontmatter is present, the rule is always loaded (backward compatible). CodeWhale has no equivalent — every instruction file is unconditionally injected.

Reference: Claude Code Rules docs

Historical context

Issue When What
#454 v0.8.8 (2026-05-02) Introduced instructions config key; goal: "Per-project overrides per-user"
#417 v0.8.8 (2026-05-02) Same hotfix branch, same day — blanket denial of project-scope instructions (security fix)

Both issues landed within 24 hours on the same hotfix branch. The design has not been revisited since.

Why the current design is flawed

Mismatched threat model

#417's threat model: a malicious repo uses .codewhale/config.toml to hijack api_key / base_url / provider, redirecting requests to an attacker-controlled endpoint.

For instructions, this model doesn't hold:

  • A malicious repo's build.rs / Makefile / package.json scripts can directly execute arbitrary commands — far more powerful than reading /etc/passwd and leaking it through the model
  • instructions poses a much lower risk than code execution, yet is denied at the same level

Trust mechanism not utilized

CodeWhale already has trust_level = "trusted" and /trust on. Once a user explicitly trusts a workspace, project-scope instructions should be allowed — trusting the repo's code implies trusting its rule files.

But merge_project_config() (main.rs:6159) does not check trust state at all; every workspace is treated the same.

Contrast with approval_policy's nuanced handling

The same function handles approval_policy and sandbox_mode with granularity — only allowing tightening, not relaxing:

if codewhale_config::project_approval_policy_is_allowed(...) {
    config.approval_policy = Some(v.to_string());
}

instructions could follow the same pattern: restrict paths to within the workspace (like AGENTS.md's security model), or gate on trust state.

Proposed solutions

These can be implemented independently or in combination:

Solution Effort Description
A. Trust gating S Check workspace trust in merge_project_config(); allow project instructions when trusted
B. Path restriction S Constrain project-scope instructions paths to the workspace subtree (paths starting with ./ that don't escape)
C. Glob support S Add glob expansion in instructions_paths(), sorting matched files by name
D. Rules dir auto-discovery M Auto-scan .codewhale/rules/ (with .claude/rules/ compat), matching Claude Code behavior
E. Conditional rule loading M Parse optional YAML frontmatter (paths, description) in rule files; skip rules whose paths glob doesn't match current workspace activity

Recommended: B + C + D + E — path restriction for safety + glob + rules directory + conditional loading, matching Claude Code's full rules experience.

Related


概述

CodeWhale 的规则系统在多项目场景下几乎不可用。instructions 配置项从 v0.8.8 起就在项目级被硬编码禁止,无 trust 感知、无 glob 支持、无 rules 目录自动发现——维护多个项目时,每新增一个规则文件都必须去全局用户配置里手动添加。

具体表现

1. 项目级 instructions 被硬编码忽略(无论 trust 状态)

# .codewhale/config.toml(项目级,不会生效!)
trust_level = "trusted"
instructions = [".claude/rules/coding-style.md", ".claude/rules/naming.md"]

用户只能把规则列在 ~/.codewhale/config.toml(全局用户级),多项目维护非常痛苦。

相关代码crates/tui/src/main.rs:6309-6315instructions 被独立硬编码拒绝,和 api_key 劫持同等对待。

2. 不支持 glob / 通配符

# 不工作!"*.md" 被当作字面文件名
instructions = [".claude/rules/*.md"]

相关代码crates/tui/src/config.rs:3591-3601instructions_paths() 只做了 expand_path(展开 ~ 和环境变量),没有 glob 展开。

3. 不支持 rules 目录自动发现

Claude Code 会自动发现 .claude/rules/ 下的所有 .md 文件。CodeWhale 没有对应机制。

相关代码crates/tui/src/project_context.rs:34-41PROJECT_CONTEXT_FILES 只有固定文件名(AGENTS.md、CLAUDE.md 等),不扫描 .codewhale/rules/.claude/rules/ 目录。

4. 不支持按需加载(YAML frontmatter + paths 匹配)

Claude Code 的 rules 支持 YAML frontmatter,通过 paths 字段控制规则是否按当前工作区活动按需加载。不相关的规则会被跳过——保持 prompt 精简,减少"Lost in the Middle"噪声。

---
paths: "src/**/*.rs"
description: "Rust 编码规范"
---

# Rust 规则
模块级常量使用 `const` 而非 `static`

如果规则文件没有 frontmatter,则始终加载(向后兼容)。CodeWhale 没有对应机制——每个指令文件无条件全量注入。

参考Claude Code Rules 文档

历史背景

Issue 时间 内容
#454 v0.8.8 (2026-05-02) 引入 instructions 配置项,设计目标:"Per-project overrides per-user"
#417 v0.8.8 (2026-05-02) 同一天同一 hotfix 分支,一刀切禁止项目级 instructions(安全修复)

两个 issue 在 24 小时内先后合入同一 hotfix 分支。此后无人重新审视这个设计。

为什么现有设计不合理

攻击面不对等

#417 的威胁模型是:恶意仓库通过 .codewhale/config.toml 劫持 api_key / base_url / provider,把请求转向攻击者服务器。

但对 instructions 来说,这个威胁模型不成立:

  • 恶意仓库的 build.rs / Makefile / package.json scripts 可以直接执行任意命令,比读取 /etc/passwd 再通过模型外泄强大得多
  • instructions 的安全风险远低于代码执行,却被放在同一档一刀切拒绝

Trust 机制未被利用

CodeWhale 已有 trust_level = "trusted"/trust on 命令。用户主动信任 workspace 后,项目级 instructions 应被允许——信任仓库代码即信任其规则文件。

merge_project_config()main.rs:6159)完全不检查 trust 状态,对所有 workspace 一视同仁。

对比 approval_policy 的精细处理

同一函数对 approval_policysandbox_mode 的处理精细得多——只允许"收紧":

if codewhale_config::project_approval_policy_is_allowed(...) {
    config.approval_policy = Some(v.to_string());
}

instructions 完全可以用同样思路:限制路径在 workspace 内(类似 AGENTS.md 的安全模型),或检查 trust 状态。

建议方案

以下方案可独立实现,也可组合:

方案 改动量 描述
A. Trust gating S merge_project_config() 检查 workspace trust 状态,trusted 时允许项目级 instructions
B. Path restriction S 项目级 instructions 路径限制在 workspace 子树内(./ 开头且解析后不逃逸 workspace)
C. Glob support S instructions_paths() 对每条路径尝试 glob 展开,匹配到的文件按文件名排序
D. Rules 目录自动发现 M 新增 .codewhale/rules/(兼容 .claude/rules/)目录自动扫描,对标 Claude Code
E. 按需加载 M 解析规则文件的可选 YAML frontmatter(pathsdescription),跳过 paths glob 不匹配当前活动的规则

推荐组合B + C + D + E — 安全约束(路径限制)+ 便捷性(glob + 目录自动发现 + 按需加载),对标 Claude Code 完整规则体验。

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions