Skip to content

Commit 73f7765

Browse files
committed
feat: linter_config_files 推全局(39/54 stable-beta 语言填满)
M8 遗留收尾:env_check 自 v0.6.5 起从 registry 读 linter_config_files, 但当时只迁移 4 条;本次按保守清单填充 27 条 + 从 requiresConfig 复制 8 条 (仅当 requiresConfig 本身就是 linter 配置:yaml/markdown/scala/erlang/ protobuf/vue/svelte/astro;java/go 等项目文件型 requiresConfig 不拷贝)。 覆盖:go/.golangci.yml、shell/.shellcheckrc、dockerfile/.hadolint.*、 ruby/.rubocop.*、php/phpstan+psalm、swift/.swiftlint.*、kotlin/detekt、 dart/analysis_options.yaml、elixir/.credo.exs、c|cpp|objc|cuda/clang 双件、 css/stylelint 系、html/.htmlhintrc、terraform/.tflint.*、sql/.sqlfluff、 haskell/.hlint.*、ocaml/.ocamlformat、perl/.perlcriticrc、 clojure/.clj-kondo/config.edn、powershell/PSScriptAnalyzerSettings.psd1、 crystal/.ameba.yml、lua/.luacheckrc、r/.lintr、ansible/.ansible-lint、 solidity/.solhint.* 等。 填错无副作用:detect_linter_config 只报告实际存在的文件。 守护测试:stable/beta 非空计数 ≥25(防重构清空),当前 39。 spec: registry-driven-config +2 Requirements 已合入 验证:unittest 我方套件 32/32(并行会话 WIP test_mcp_server/ test_gate_trigger 未跟踪且 6 失败属其开发中,不在本 PR)+ run_all 141/141/0 + validator 0 errors + diff clean
1 parent 0689fe2 commit 73f7765

8 files changed

Lines changed: 325 additions & 11 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-09-22
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
## Context
2+
3+
- `hooks/env_check.py::detect_linter_config`(M8 后):`for lang_id, lang_def in REGISTRY.items(): files = lang_def.get("linter_config_files") or []` → 存在性过滤 → `{lang: [files]}`。**填错无害**(不存在的文件名永远不命中),这是本 change 敢于「保守估计 + 宽清单」的依据。
4+
- 现状覆盖:java / rust / typescript / python 四条(M8 迁移自旧硬编码常量)。
5+
- 关键区分(M8 design 遗留):`requiresConfig` **不等于** linter 配置——java 的 `requiresConfig` 是 `pom.xml` 等项目文件(gate 判定「项目是否接入」用),**不是** linter 配置文件;直接把 requiresConfig 拷进 linter_config_files 会造成误报(SessionStart 会说「linter 配置:pom.xml」)。只有 requiresConfig 本身就是 linter 配置的条目(yaml 的 `.yamllint*`、markdown 的 `.markdownlint*`、scala 的 `.scalafmt.conf`、erlang 的 `elvis.config*`、protobuf 的 `buf.yaml`、vue/svelte/astro 的 eslint 系)可直接复制。
6+
7+
## Decisions
8+
9+
### 保守清单(只填确定存在的标准配置名)
10+
11+
按语言分组(~30 条):
12+
13+
- go: `.golangci.yml`, `.golangci.yaml`
14+
- shell: `.shellcheckrc`
15+
- dockerfile: `.hadolint.yaml`, `.hadolint.yml`
16+
- yaml: `.yamllint`, `.yamllint.yaml`, `.yamllint.yml`(requiresConfig 即 linter 配置,直接复制)
17+
- markdown: `.markdownlint-cli2.*` / `.markdownlint.*` 九件套(同上复制)
18+
- vue / svelte / astro: eslint 系(requiresConfig 直接复制)
19+
- ruby: `.rubocop.yml`, `.rubocop.yaml`
20+
- php: `phpstan.neon`, `phpstan.neon.dist`, `psalm.xml`
21+
- swift: `.swiftlint.yml`, `.swiftlint.yaml`
22+
- kotlin: `detekt.yml`, `detekt.yaml`
23+
- dart: `analysis_options.yaml`
24+
- elixir: `.credo.exs`
25+
- c / cpp / objc / cuda: `.clang-tidy`, `.clang-format`
26+
- css: `.stylelintrc`, `.stylelintrc.json`, `.stylelintrc.yml`, `stylelint.config.js`
27+
- html: `.htmlhintrc`
28+
- terraform: `.tflint.hcl`, `tflint.hcl`
29+
- sql: `.sqlfluff`
30+
- haskell: `.hlint.yaml`, `.hlint.yml`
31+
- ocaml: `.ocamlformat`
32+
- perl: `.perlcriticrc`
33+
- clojure: `.clj-kondo/config.edn`
34+
- powershell: `PSScriptAnalyzerSettings.psd1`
35+
- crystal: `.ameba.yml`
36+
- lua: `.luacheckrc`
37+
- r: `.lintr`
38+
- ansible: `.ansible-lint`
39+
- solidity: `.solhint.json`, `.solhintrc`
40+
- scala: `.scalafmt.conf`(requiresConfig 复制)
41+
- erlang: `elvis.config`, `elvis.config.js`(requiresConfig 复制)
42+
- protobuf: `buf.yaml`, `buf.gen.yaml`, `buf.work.yaml`(requiresConfig 复制)
43+
44+
**明确不填**(低置信或无标准配置名):graphql、toml、zig、nim、julia、pascal、liquid、luau、nix、groovy、cfml、fsharp、vbnet、csharp、elm、objc→已填 clang、cfml 等——留空数组是诚实状态。
45+
46+
### 守护测试
47+
48+
`test_linter_config_files_coverage`:stable/beta 中非空 `linter_config_files` 数量 ≥ 25(当前应 ~34)。防止未来语言注册表重构静默清空填充。
49+
50+
### 不动 requiresConfig 语义
51+
52+
两条字段语义分离是 M8 的设计决定,本 change 遵守:requiresConfig 服务 gate 判定,linter_config_files 服务 SessionStart 盘点。
53+
54+
## Risks / Trade-offs
55+
56+
- 个别配置名猜错 → 该文件不存在 → 盘点不报 → 无副作用。
57+
- 个别语言存在更流行的配置名(如 graphql 的 `.graphqlrc`)→ 未填 → 盘点少报一项 → 未来按需补,本 change 不冒进。
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Why
2+
3+
M8(v0.6.5)把 `hooks/env_check.py` 的 linter 配置盘点改为从 `scripts/languages.json` 的 `linter_config_files` 字段读取,但当时只迁移了 java/rust/typescript/python 四条历史记录——54 条 stable/beta 语言中仍有约 30 条填着默认空数组,SessionStart 钩子对它们的项目依旧报告「未配置任何 linter」,即使项目里躺着 `.golangci.yml`、`.rubocop.yml`、`.shellcheckrc` 等真实配置。这就是评审遗留的「detect_linter_config 推全局」。
4+
5+
## What Changes
6+
7+
- `scripts/languages.json`:为约 30 条 stable/beta 语言填充 `linter_config_files`(**保守清单**:只填确定存在的业界标准配置文件名,如 `.golangci.yml` / `.shellcheckrc` / `.rubocop.yml` / `.swiftlint.yml` / `analysis_options.yaml` / `.credo.exs` / `.clang-tidy` / `.stylelintrc*` / `.shellcheckrc` 等)。填错无副作用——`detect_linter_config` 只报告**实际存在**的文件,永不误报;填漏只影响盘点广度,不影响正确性。
8+
- `tests/test_validate_languages_json.py` 新增 1 条守护:stable/beta 语言中非空 `linter_config_files` 的数量 ≥ 25(防止未来重构把填充结果清空)。
9+
- 不改 `hooks/env_check.py`(M8 已 registry 驱动);不改 `scripts/languages.json` 其他字段;不改 `docs/LANGUAGES.md` 生成逻辑(生成器不读该字段,已实测)。
10+
11+
## Capabilities
12+
13+
### New Capabilities
14+
15+
None.
16+
17+
### Modified Capabilities
18+
19+
- `registry-driven-config`: 新增 Requirement——已知配置文件名的 stable/beta 语言 SHALL 填充该字段,SessionStart 盘点覆盖面由数据而非代码决定。
20+
21+
## Impact
22+
23+
约 30 条语言 JSON 条目各加 1-4 个字符串;1 条测试;spec delta 1 条 Requirement + 2 Scenario。零运行时行为变化(除 SessionStart 报告更全)。
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Known linter config filenames SHALL be populated for stable/beta languages
4+
5+
For each stable/beta language whose linter has well-known project-level configuration filenames, `scripts/languages.json` SHALL populate that entry's `linter_config_files` with those filenames. The population SHALL be conservative: only filenames that genuinely exist as that linter's configuration. Entries whose linter has no standard config filename (or where the filename is uncertain) MAY keep the empty list. `hooks/env_check.py` SHALL continue to report only files that actually exist, so an over-guessed filename never produces a false report.
6+
7+
#### Scenario: A Go project carries .golangci.yml
8+
9+
- **WHEN** the SessionStart hook runs in a repository whose root contains `.golangci.yml`
10+
- **THEN** `detect_linter_config` reports `{"go": [".golangci.yml"]}` because `go.linter_config_files` includes it
11+
12+
#### Scenario: A guessed filename does not exist in the project
13+
14+
- **WHEN** a language lists a config filename that the current project does not contain
15+
- **THEN** the hook reports nothing for that language/file pair — existence filtering prevents false positives
16+
17+
### Requirement: Linter config coverage SHALL be guarded against regression
18+
19+
`tests/test_validate_languages_json.py` SHALL assert that at least 25 stable/beta languages have a non-empty `linter_config_files` list, so a registry refactor cannot silently wipe the populated coverage.
20+
21+
#### Scenario: A refactor drops the populated fields
22+
23+
- **WHEN** a future edit removes `linter_config_files` from most stable/beta entries
24+
- **THEN** the coverage test fails with the current populated count
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## 1. Data fill
2+
3+
- [x] 1.1 Apply the conservative per-language map from design.md to `scripts/languages.json` (~30 stable/beta entries).
4+
- [x] 1.2 `python3 scripts/validate_languages_json.py` — exit 0 (type rule covers the new values).
5+
6+
## 2. Coverage guard test
7+
8+
- [x] 2.1 Add `test_linter_config_files_coverage` (≥25 stable/beta with non-empty list) to `tests/test_validate_languages_json.py`.
9+
10+
## 3. Validation
11+
12+
- [x] 3.1 `python3 -m unittest discover -s tests -p 'test_*.py' -v` — all pass.
13+
- [x] 3.2 `python3 tests/run_all.py` — 141 / 0 / 0.
14+
- [x] 3.3 `git diff --check` — clean; `openspec validate --all --strict` — pass.
15+
16+
## 4. Release
17+
18+
- [x] 4.1 bump patch → 0.6.8; in-repo marketplace pins → v0.6.8; feat PR → merge; release PR → merge; tag v0.6.8; market repo catalog + README row → 0.6.8.
19+
- [x] 4.2 `openspec archive fill-linter-config-files`.

‎openspec/specs/registry-driven-config/spec.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,26 @@ Every language entry in `scripts/languages.json` SHALL have a `linter_config_fil
2626
- **WHEN** the project's root contains `ruff.toml` but no other linter configs
2727
- **THEN** `env_check.detect_linter_config` returns `{"python": ["ruff.toml"]}` (or equivalent from the registry) and excludes all other languages
2828

29+
### Requirement: Known linter config filenames SHALL be populated for stable/beta languages
30+
31+
For each stable/beta language whose linter has well-known project-level configuration filenames, `scripts/languages.json` SHALL populate that entry's `linter_config_files` with those filenames. The population SHALL be conservative: only filenames that genuinely exist as that linter's configuration. Entries whose linter has no standard config filename (or where the filename is uncertain) MAY keep the empty list. `hooks/env_check.py` SHALL continue to report only files that actually exist, so an over-guessed filename never produces a false report.
32+
33+
#### Scenario: A Go project carries .golangci.yml
34+
35+
- **WHEN** the SessionStart hook runs in a repository whose root contains `.golangci.yml`
36+
- **THEN** `detect_linter_config` reports `{"go": [".golangci.yml"]}` because `go.linter_config_files` includes it
37+
38+
#### Scenario: A guessed filename does not exist in the project
39+
40+
- **WHEN** a language lists a config filename that the current project does not contain
41+
- **THEN** the hook reports nothing for that language/file pair — existence filtering prevents false positives
42+
43+
### Requirement: Linter config coverage SHALL be guarded against regression
44+
45+
`tests/test_validate_languages_json.py` SHALL assert that at least 25 stable/beta languages have a non-empty `linter_config_files` list, so a registry refactor cannot silently wipe the populated coverage.
46+
47+
#### Scenario: A refactor drops the populated fields
48+
49+
- **WHEN** a future edit removes `linter_config_files` from most stable/beta entries
50+
- **THEN** the coverage test fails with the current populated count
51+

0 commit comments

Comments
 (0)