docs: CLAUDE.md 补 project-specific 工作流校准

~/.claude/CLAUDE.md 已经定义 Codex-first 路由、Codex MCP 强制参数、
Linus 三问、4-phase 工作流、prompt 模板、auto-confirm/pause 边界。本
文件不重复这些全局规则, 只加项目级校准:

- 路由表: 用本仓库实际任务举例 (GIN_MODE 接线 / loadDotenv 新增 /
  S1017 修 / .gitignore 行 / CLAUDE.md 自身 / 注释 typo / .env.example
  标题), 给出"trivial 阈值"在本项目的具体判据
- Codebase 模式: 指向 internal/httpclient/redirect_test.go (httptest
  + "127.0.0.1" 白名单), internal/logging/tool_call_test.go
  (bytes.Buffer + slog.NewJSONHandler 捕获), internal/mcp/tools/
  目录约定, 让未来 agent 进包前先读已有测试文件
- 预提交检查 (项目级): 强调 deferred-behavior test 是 gap-locking
  不是回归, 公共签名变更必须在 commit body 解释兼容性
- commit 消息规约: 从 git log 实际样本里抽出 <package>: 中文动词
  开头 + body 回答"gap / fix / 风险 / 锁定测试" + Co-Authored-By
- 项目坑: .env 不透明 / internal/ Go 强制 / multiHandler 双写
  (新事件 = 一次 base.Info 调用) / TestDoWithRedirect_DeferredRFC9110Gaps
  是 gap 测试不是 bug 报告

仅改 CLAUDE.md, 0 代码变更, 无需 build/test 验证.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-13 18:14:40 +08:00
co-authored by Claude
parent 475e353850
commit 210aeb1315
+70
View File
@@ -29,6 +29,8 @@ Standard Go toolchain (Go 1.25.5 per `go.mod`). All commands run from the repo r
There is no `Makefile`, no `golangci-lint` config, and no test files yet — do not assume they exist.
Cross-compile outputs are gitignored via the pattern `spark-mcp-*-*` (see `.gitignore`); do not commit stray build artifacts.
## Key Pinned Dependencies
`go.mod` declares these as `// indirect` (they will become direct once code is added that imports them):
@@ -56,3 +58,71 @@ The codebase is empty enough that a few decisions are not yet pinned down and wi
## Memory
This is a fresh repo with no `MEMORY.md` or project-level memory file. Decisions made during early development (transport choice, Spark backend interface, persistence schema) are worth recording once they are made — see the `claude-md-management` skill conventions.
---
# Working in this Repo
The rules in `~/.claude/CLAUDE.md` apply (Codex-first routing, mandatory Codex MCP invocation parameters, Linus three-question review, 4-phase workflow, Codex prompt template, auto-confirm vs pause boundaries). This section adds the **project-specific calibration** for applying them here.
## Routing: Codex vs CC
| Task in this repo | Executor | Why |
|---|---|---|
| Edit `main.go` to wire `cfg.GinMode` | Codex | One-line code change in production path |
| Add `loadDotenv` to `internal/config/config.go` | Codex | New function, real logic |
| Add a new test file | Codex | Even small, code generation is the strength |
| Edit `internal/httpclient/ssrf.go` to silence S1017 | Codex | Behavior must stay identical — needs verification |
| Update `.gitignore` line | CC | Single-line, no logic, mechanical |
| Update this `CLAUDE.md` | CC | Pure docs |
| Fix typo in a comment | CC | <20 lines, no logic |
| Update `.env.example` header comment | CC | Single line of prose |
**Threshold for "trivial enough for CC" in this repo:** purely textual, no code generation, <20 lines, no risk of subtle behavior change. When in doubt, default to Codex — the cost difference is small and Codex is more consistent at preserving invariants.
## Codebase Patterns to Reuse
When working in a package, **read the package's existing test file first** to see the established style before writing new code:
- **HTTP client mocks** — `internal/httpclient/redirect_test.go` uses `httptest.NewServer` with path-switch handlers. For SSRF / redirect tests, pass `"127.0.0.1"` as the allowed host.
- **slog capture in tests** — `internal/logging/tool_call_test.go` shows the pattern: `bytes.Buffer` + `slog.NewJSONHandler`, decode each line with `json.Unmarshal`, assert on `rec["msg"]`, `rec["level"]`, `rec["tool"]`. Reuse this for any new logger test.
- **MCP tool registration** — every tool lives in `internal/mcp/tools/<name>.go`. Schema is the first arg, handler is a method on the package's client struct. The tool's Go test file goes in the same directory.
## Pre-Commit Checklist (project-specific)
In addition to the global acceptance list, this repo's convention is:
- [ ] `go build ./...` — zero errors
- [ ] `go vet ./...` — zero warnings (catches the stringsseq / S1017 family)
- [ ] `go test ./...` — all green; new tests added for new behavior
- [ ] `gofmt -l .` — empty output (no diffs pending)
- [ ] No stray `spark-mcp-*-*` binary in repo root
- [ ] If behavior gap is intentionally deferred: add a deferred test that **locks the current behavior** (so a future fix must update the test deliberately) and a `TODO(<area>)` comment in the source with rationale
- [ ] If a public function signature changed: explain backward compatibility in the commit body
## Commit Message Convention
Observed in this repo's recent history:
```
<package>: <verb-led summary in Chinese>
<optional body: motivation, design tradeoffs, why-not-fix-X, what tests were added>
<optional "未提交: <file>" trailer for intentionally uncommitted artifacts>
Co-Authored-By: Claude <noreply@anthropic.com>
```
- Subject line: `<package>:` prefix (e.g. `httpclient:`, `rm:`, `admin:`, `config:`, `logging:`, `gitignore:`), then a short verb-led summary in Chinese.
- Body in Chinese; technical English terms (RFC numbers, status codes, test names, package paths) are fine in code form.
- Always end with the `Co-Authored-By` trailer.
- Body should answer: *what was the gap, why this fix, what could it have broken, what tests lock it in.* This is what makes the commit history a useful design record.
## Project-Specific Gotchas
- **`.env` is uncommitted and gitignored** — never try to read it, and never claim to know what's in it. Treat its existence as opaque.
- **Cross-compile artifacts** — `go build -o spark-mcp-linux-amd64 .` and similar produce files covered by the `spark-mcp-*-*` gitignore. Don't try to `git add` them.
- **`internal/` is enforced** — Go's `internal/` rule means sub-packages of `spark-mcp-go/internal/...` are importable only by `spark-mcp-go/...`. Cross-repo imports will fail; design packages accordingly.
- **Deferred-behavior tests are intentional** — `TestDoWithRedirect_DeferredRFC9110Gaps` is a *gap-locking* test, not a regression test. The assertions verify the gap, not the fix. Read the comment block above the test before "fixing" what looks like a bug.
- **YARN / SHS API surface is the source of truth** — the upstream REST API behavior (e.g. amContainerLogs being a 307 redirect, not a 303) drives the design. Don't invent redirect semantics; check what the cluster actually returns.
- **`log/slog` uses a `multiHandler` for dual terminal+file writing** — the `base *slog.Logger` passed to `StartToolCall` already routes through the main logger. Adding a new event is one `base.Info(...)` call; do not add a separate handler.