فهرست منبع

feat: Fmode v2 双端客户端 — 工程底座与桌面端 UI 完成

工程底座
- Angular 20.3 + Tailwind 3.4(preflight 启用)+ ui-kit 官方组件底座整目录接入
- 设计令牌:4 套整体配色 × 明暗(ADR-006);令牌名沿用 ui-kit,取值对齐敲定原型
- 数据访问抽象层 FmodeApi + MockApi/ParseApi 单开关;云函数未就绪时走显式抛错占位

桌面端(9 页 + 登录 + 导航 + 外壳)
- 会话 / 通讯录 / 项目 / 事件 / 工作台 / 任务看板 / 成果 / 数字生命详情 / 我的
- 全部用 ui-kit 组件拼装(30 个不同组件),零硬编码色值
- 流动布局:栏宽比例 + 最小宽兜底;列表页固定头部 + 区域内滚动
- 右栏可切换「任务 / 成果」双面板,成果支持内联预览与下载

规范与质量
- verify:ui 断言 11 项全通过(硬编码色值/令牌单轨/字号档位/圆角档位/覆盖行数/
  服务单例/ui-kit 覆盖率/触达区/console 残留/焦点环/断点令牌化)
- 289 单测通过;构建 451KB(gzip 114KB)
- 规范包:rules 21 模块 / templates / 60 技能 / docs 全链文档

遗留
- 移动端 11 页沿用首版实现,待按同一范式重做
- 云函数未接;smoke / verify:layout 脚本待建;数据范式文档待补

Co-Authored-By: Claude Code <noreply@anthropic.com>
Huccct 1 روز پیش
والد
کامیت
362a01151a
100فایلهای تغییر یافته به همراه15671 افزوده شده و 0 حذف شده
  1. 55 0
      .claude/skills/ask-matt/PHASE-BOUNDARIES.md
  2. 90 0
      .claude/skills/ask-matt/SKILL.md
  3. 5 0
      .claude/skills/ask-matt/agents/openai.yaml
  4. 164 0
      .claude/skills/brainstorming/SKILL.md
  5. 214 0
      .claude/skills/brainstorming/scripts/frame-template.html
  6. 88 0
      .claude/skills/brainstorming/scripts/helper.js
  7. 354 0
      .claude/skills/brainstorming/scripts/server.cjs
  8. 148 0
      .claude/skills/brainstorming/scripts/start-server.sh
  9. 56 0
      .claude/skills/brainstorming/scripts/stop-server.sh
  10. 49 0
      .claude/skills/brainstorming/spec-document-reviewer-prompt.md
  11. 287 0
      .claude/skills/brainstorming/visual-companion.md
  12. 18 0
      .claude/skills/claude-handoff/SKILL.md
  13. 5 0
      .claude/skills/claude-handoff/agents/openai.yaml
  14. 87 0
      .claude/skills/code-review/SKILL.md
  15. 3 0
      .claude/skills/code-review/agents/openai.yaml
  16. 120 0
      .claude/skills/code/SKILL.md
  17. 6 0
      .claude/skills/code/_meta.json
  18. 48 0
      .claude/skills/code/criteria.md
  19. 42 0
      .claude/skills/code/execution.md
  20. 38 0
      .claude/skills/code/memory-template.md
  21. 31 0
      .claude/skills/code/planning.md
  22. 60 0
      .claude/skills/code/state.md
  23. 39 0
      .claude/skills/code/verification.md
  24. 37 0
      .claude/skills/codebase-design/DEEPENING.md
  25. 44 0
      .claude/skills/codebase-design/DESIGN-IT-TWICE.md
  26. 114 0
      .claude/skills/codebase-design/SKILL.md
  27. 3 0
      .claude/skills/codebase-design/agents/openai.yaml
  28. 63 0
      .claude/skills/coding-guide/SKILL.md
  29. 671 0
      .claude/skills/dev-expert/FAQ.md
  30. 369 0
      .claude/skills/dev-expert/README.md
  31. 545 0
      .claude/skills/dev-expert/SKILL.md
  32. 6 0
      .claude/skills/dev-expert/_meta.json
  33. 29 0
      .claude/skills/dev-expert/hooks.capture-draft.json
  34. 161 0
      .claude/skills/dev-expert/hooks.json
  35. 100 0
      .claude/skills/dev-expert/hooks/backup_on_write.py
  36. 193 0
      .claude/skills/dev-expert/hooks/capture_learning.py
  37. 502 0
      .claude/skills/dev-expert/hooks/cms_risk_check.py
  38. 65 0
      .claude/skills/dev-expert/hooks/debug_residue.py
  39. 74 0
      .claude/skills/dev-expert/hooks/dep_scan.py
  40. 47 0
      .claude/skills/dev-expert/hooks/guard_dirs.py
  41. 190 0
      .claude/skills/dev-expert/hooks/handoff_snapshot.py
  42. 156 0
      .claude/skills/dev-expert/hooks/install_hooks.py
  43. 111 0
      .claude/skills/dev-expert/hooks/lint_on_write.py
  44. 81 0
      .claude/skills/dev-expert/hooks/php8_compat.py
  45. 148 0
      .claude/skills/dev-expert/hooks/plan_guard.py
  46. 134 0
      .claude/skills/dev-expert/hooks/recurrence_promote.py
  47. 69 0
      .claude/skills/dev-expert/hooks/secret_scan.py
  48. 87 0
      .claude/skills/dev-expert/hooks/security_scan.py
  49. 83 0
      .claude/skills/dev-expert/hooks/select_star.py
  50. 196 0
      .claude/skills/dev-expert/hooks/sql_injection_check.py
  51. 190 0
      .claude/skills/dev-expert/hooks/step_ref_check.py
  52. 56 0
      .claude/skills/dev-expert/hooks/utf8_check.py
  53. 221 0
      .claude/skills/dev-expert/references/api-design.md
  54. 116 0
      .claude/skills/dev-expert/references/architecture-decision.md
  55. 259 0
      .claude/skills/dev-expert/references/bug-diagnosis.md
  56. 422 0
      .claude/skills/dev-expert/references/cms-development.md
  57. 313 0
      .claude/skills/dev-expert/references/code-generation.md
  58. 358 0
      .claude/skills/dev-expert/references/code-review.md
  59. 107 0
      .claude/skills/dev-expert/references/delivery-assurance.md
  60. 129 0
      .claude/skills/dev-expert/references/distributed-systems.md
  61. 187 0
      .claude/skills/dev-expert/references/doc-generation.md
  62. 135 0
      .claude/skills/dev-expert/references/domain-driven-design.md
  63. 104 0
      .claude/skills/dev-expert/references/error-ledger.md
  64. 196 0
      .claude/skills/dev-expert/references/execution-safety.md
  65. 436 0
      .claude/skills/dev-expert/references/frontend-design.md
  66. 145 0
      .claude/skills/dev-expert/references/java-development.md
  67. 262 0
      .claude/skills/dev-expert/references/javascript-development.md
  68. 378 0
      .claude/skills/dev-expert/references/karpathy-coding-guidelines.md
  69. 92 0
      .claude/skills/dev-expert/references/laravel-development.md
  70. 114 0
      .claude/skills/dev-expert/references/laravel-testing.md
  71. 355 0
      .claude/skills/dev-expert/references/mysql-database.md
  72. 183 0
      .claude/skills/dev-expert/references/performance-benchmark.md
  73. 144 0
      .claude/skills/dev-expert/references/project-knowledge-graph.md
  74. 482 0
      .claude/skills/dev-expert/references/project-memory-management.md
  75. 170 0
      .claude/skills/dev-expert/references/refactoring.md
  76. 267 0
      .claude/skills/dev-expert/references/software-project.md
  77. 228 0
      .claude/skills/dev-expert/references/spec-driven-development.md
  78. 76 0
      .claude/skills/dev-expert/references/style-alignment.md
  79. 232 0
      .claude/skills/dev-expert/references/task-decomposition-and-execution.md
  80. 191 0
      .claude/skills/dev-expert/references/tech-selection.md
  81. 201 0
      .claude/skills/dev-expert/references/test-generation.md
  82. 259 0
      .claude/skills/dev-expert/references/website-project.md
  83. 1410 0
      .claude/skills/dev-expert/scripts/build_graph.py
  84. 138 0
      .claude/skills/diagnosing-bugs/SKILL.md
  85. 3 0
      .claude/skills/diagnosing-bugs/agents/openai.yaml
  86. 44 0
      .claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh
  87. 182 0
      .claude/skills/dispatching-parallel-agents/SKILL.md
  88. 47 0
      .claude/skills/domain-modeling/ADR-FORMAT.md
  89. 60 0
      .claude/skills/domain-modeling/CONTEXT-FORMAT.md
  90. 74 0
      .claude/skills/domain-modeling/SKILL.md
  91. 3 0
      .claude/skills/domain-modeling/agents/openai.yaml
  92. 70 0
      .claude/skills/executing-plans/SKILL.md
  93. 200 0
      .claude/skills/finishing-a-development-branch/SKILL.md
  94. 95 0
      .claude/skills/git-guardrails-claude-code/SKILL.md
  95. 3 0
      .claude/skills/git-guardrails-claude-code/agents/openai.yaml
  96. 25 0
      .claude/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh
  97. 7 0
      .claude/skills/grill-me/SKILL.md
  98. 5 0
      .claude/skills/grill-me/agents/openai.yaml
  99. 7 0
      .claude/skills/grill-with-docs/SKILL.md
  100. 5 0
      .claude/skills/grill-with-docs/agents/openai.yaml

+ 55 - 0
.claude/skills/ask-matt/PHASE-BOUNDARIES.md

@@ -0,0 +1,55 @@
+# Phase boundaries
+
+A **phase** is a chunk of work inside a session — the grilling, the implementation, the QA. The definition is fuzzy on purpose: a phase ends when you think *"ok, we're done with that"*.
+
+The **phase boundary** is the gap between two phases, and it is the only place this decision belongs. Mid-phase there is no decision to make — continue, or split the work that's left into subagents. Compacting mid-phase makes the agent lose the thread.
+
+## The five options
+
+| Option       | What it does                                                    |
+| ------------ | --------------------------------------------------------------- |
+| **Continue** | Stay in the session. No context switch at all.                    |
+| **`/clear`** | Empty the context window and start from nothing.                  |
+| **`/handoff`** | Write a portable markdown file and seed a session anywhere with it. |
+| **Subagent** | Send the task to its own context window and get a report back.     |
+| **`/compact`** | Compress this context and seed a fresh session with the summary.  |
+
+## The tree
+
+Work top to bottom at the boundary. The first **yes** wins.
+
+**1. Can you continue in this session?** Two things make the answer yes: the next phase needs this phase as a **primary source**, or you have enough [smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone) left (~150k tokens) for the next phase to fit. Grilling → implementation is the standard yes: the implementation wants the reasoning verbatim, not a summary of it. Continue costs nothing and loses nothing, so rule it out before anything else.
+
+**2. Is the context irrelevant to what comes next?** Is everything in this session — the exploration, the decisions, the dead ends — disposable? If so, **`/clear`**. It is the cheapest move on the board: it takes no time and hands back the whole window. `/clear` also isn't terminal — the old session stays resumable.
+
+The cost of getting this wrong is one-way. Clear a *relevant* context and you lose the **why** behind what you built, and no amount of reading the diff back gets it returned.
+
+**3. Do you need to hand off?** `/handoff` is narrow. You need it only when you are:
+
+- swapping to a **new harness** (Claude → Codex),
+- moving to a **new directory** or repo,
+- sending the work to a **colleague**,
+- or forking a side task you found **mid-phase** without derailing what you're doing.
+
+That list is the whole clause. What `/handoff` buys is **portability** — a file that travels. If nothing is travelling, you don't need it.
+
+**4. Can the task be done AFK?** Is it scoped tightly enough to run with you away from the keyboard, no steering? Then send it to a **subagent** and leave this session untouched. Automated review is the standard case: the agent reads the diff and reports, and you aren't needed while it does.
+
+**5. Otherwise, `/compact`.** Relevant context, same harness, same directory, and you need to stay in the loop — this is where the tree lands, and it lands here often. Pass it an instruction (`/compact we're going to QA this area`) so the summary keeps what the next phase needs.
+
+`/compact` is the **default, not the first reach**. It sits at the bottom because the four questions above it are all cheaper or more precise. The failure mode when people start here is a fresh session that is confidently wrong about a decision the summary flattened.
+
+## Primary and secondary sources
+
+Every move except **Continue** turns a **primary source** into a **secondary source** — the session as it happened, replaced by a summary of it. The trade is always the same shape:
+
+| Source                            | Information | Noise | Room to move |
+| --------------------------------- | ----------- | ----- | ------------ |
+| Primary (Continue)                | Full        | Lots  | Little       |
+| Secondary (`/compact`, `/handoff`) | Lossy       | Less  | Lots         |
+
+This is why question 1 comes first. You only pay the lossiness when staying costs more than it saves.
+
+## These are judgement calls
+
+The questions are not objective — each has taste in it, and the same boundary can go two ways on two days. The value is in asking them **in order**, at the boundary rather than in the middle of the work.

+ 90 - 0
.claude/skills/ask-matt/SKILL.md

@@ -0,0 +1,90 @@
+---
+name: ask-matt
+description: Ask which skill or flow fits your situation. A router over the skills in this repo.
+disable-model-invocation: true
+---
+
+# Ask Matt
+
+You don't remember every skill, so ask.
+
+A **flow** is a path through the skills. Most paths run along one **main flow**, and two **on-ramps** merge onto it. Everything else is standalone, or a vocabulary layer that runs underneath.
+
+## The main flow: idea → ship
+
+The route most work travels. You have an idea and want it built.
+
+1. **`/grill-with-docs`** — sharpen the idea by interview. Start here whenever you are **working in a working directory**: it's stateful, retaining what it learns in `CONTEXT.md` and ADRs. (No working directory? Use `/grill-me` — see Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail, which makes it the better of the two whenever a repo is there to leave it in.)
+2. **Branch — can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (a prototype lives in its own directory, which is exactly what `/handoff` is for — see Phase boundaries):
+   - **`/handoff`** out, then open a fresh session against that file,
+   - **`/prototype`** to answer the question with throwaway code,
+   - **`/handoff`** back what you learned, and reference it from the original idea thread.
+3. **Branch — is this a multi-session build?**
+   - **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. On a local tracker that's one file per ticket under `.scratch/<feature>/issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed — kick off **`/implement`** per ticket, **`/clear`ing context between each one**. Each ticket is self-contained, so the last one's context is disposable.
+   - **No** → **`/implement`** right here, in the same context window.
+
+   Either way, **`/implement`** builds each issue by driving **`/tdd`** internally — one red-green slice at a time — then closes out by running **`/code-review`**, a two-axis review (Standards + Spec) of the diff, before committing. Reach for **`/tdd`** on its own when you just want to build a concrete behaviour test-first without a full spec, and **`/code-review`** on its own whenever you want to review a branch or PR against a fixed point.
+
+### Context hygiene
+
+Keep steps 1–3 in **one unbroken context window** — don't compact or clear until after `/to-tickets` — so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket.
+
+The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~150k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded — `/compact` at the nearest phase boundary and carry on (see Phase boundaries).
+
+## On-ramps
+
+A starting situation that generates work, then merges onto the main flow.
+
+- **Bugs and requests piling up** → **`/triage`**. It moves issues through triage roles and produces agent-ready issues, which **`/implement`** later picks up.
+
+  Triage is only for issues **you didn't create** — bug reports, incoming feature requests, anything that arrives raw. Tickets that `/to-tickets` produced are already agent-ready, so **don't triage them**.
+
+- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** — one command that already goes red on *this* bug — then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the real finding is that there's no good seam to lock the bug down.
+
+- **A huge, foggy effort — a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time — producing **decisions, not deliverables** — until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't — and it's slower and denser, so save it for exactly that, never a well-scoped feature.
+
+  When the map clears, **it hands off, it doesn't build**: merge onto the main flow at **`/to-spec`**, which collapses the map's linked decisions into a buildable plan, then `/to-tickets` and `/implement` as usual. Looping the map straight into `/implement` skips that collapse and throws the linked detail away — go straight to `/implement` only when the effort turned out genuinely small.
+
+## Codebase health
+
+Not feature work — upkeep.
+
+- **`/improve-codebase-architecture`** — run whenever you have a spare moment to keep the codebase good for agents to operate in. It surfaces **deepening opportunities**; picking one _generates an idea_ you can take into the main flow at `/grill-with-docs`. It's the survey that finds the candidates; **`/codebase-design`** (below) is the bench you design the chosen one on.
+
+## Vocabulary underneath
+
+Two model-invoked references that run *beneath* the other skills — each the single source of truth for its vocabulary. Reach for them directly when the **words**, not the process, are the problem; or let the skills above pull them in.
+
+- **`/domain-modeling`** — sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `CONTEXT.md` a clean glossary.
+- **`/codebase-design`** — the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) for designing a module's *shape*: a lot of behaviour behind a small interface at a clean seam. `/tdd` and `/improve-codebase-architecture` both speak it.
+
+## Phase boundaries
+
+A **phase** is a chunk of work inside a session — the grilling, the implementation, the QA. At the **boundary** between two of them you have five options, and picking between them is the fuzziest decision in this whole map:
+
+- **Continue** — stay put. Costs nothing, loses nothing.
+- **`/clear`** — empty the window, when nothing here matters to what's next.
+- **`/handoff`** — write a portable markdown file. Narrow: only for a **new harness**, a **new directory**, a **colleague**, or forking a side task **mid-phase**. What it buys is portability.
+- **Subagent** — send a tightly-scoped task to its own window and get a report back.
+- **`/compact`** — compress this context and seed a fresh session with it. The **default**, at the bottom of the tree rather than the first reach.
+
+Read [PHASE-BOUNDARIES.md](PHASE-BOUNDARIES.md) for the ordered tree — the five questions, the reasoning behind each branch, and why the primary-source cost makes **Continue** the one to rule out first. Make the decision **at** a boundary; mid-phase, continue or split the rest into subagents.
+
+## Standalone
+
+Off the main flow entirely.
+
+- **`/grill-me`** — the same relentless interview as `/grill-with-docs`, but **stateless**: it saves nothing locally and builds no `CONTEXT.md`. Reach for it when you are **not working in a working directory** — sharpening a plan, a design, a piece of writing, anything with no repo under it. If you are in a working directory, use `/grill-with-docs` instead: it runs the same interview and leaves a paper trail, so it is strictly the better one.
+- **`/grilling`** — the interview primitive itself: rounds, the frontier, facts are the agent's job and decisions are yours. `/grill-me` and `/grill-with-docs` are the two named ways in, and `/triage`, `/wayfinder` and `/improve-codebase-architecture` all run it internally. Reach for it directly only when you want the interview with no wrapper around it.
+- **`/resolving-merge-conflicts`** — work an in-progress merge or rebase conflict hunk by hunk, resolving by **intent** traced to each side's primary source rather than by picking lines, then finish the operation. It never runs `--abort`. Standalone and off every flow: reach for it when you are already mid-conflict.
+- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway is a constraint on how the code is written, not a promise to destroy it: the answer folds into the real code, and the prototype itself is kept as a **primary source** on a `prototype/<name>` branch out of main, pointed at from the implementation issue. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
+- **`/research`** — delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs` — research feeds the thinking, it doesn't replace it.
+- **`/to-questionnaire`** — when the thing blocking you isn't in your head or the codebase but in **someone else's**, this writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** — who it's going to, what you need back — and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`.
+- **`/wizard`** — for the steps only a **human** can take: provisioning infrastructure, setting up credentials or CI secrets, clicking through an unfamiliar third-party dashboard, running a one-off migration or cutover. It generates an interactive bash script that opens each URL, captures each value, and writes it into `.env` and GitHub secrets — so the procedure stops being something you re-explain to an agent every time. Model-invoked, so the agent reaches for it the moment it hits a wall only you can pass. If the agent could just do it itself, it should; this is for where a human is genuinely in the loop.
+- **`/wait-what`** — the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `CONTEXT.md` vocabulary. It works after the fact; `/grill-with-docs` is the upfront cure, because a shared language agreed early is what stops the jargon arriving at all.
+- **`/teach`** — learn a concept over multiple sessions, using the current directory as a stateful workspace.
+- **`/writing-for-agents`** — reference for writing documents agents consume: skills, AGENTS.md, pointed-at docs.
+
+## Precondition
+
+**`/setup-matt-pocock-skills`** — run before your first engineering flow to configure the issue tracker, triage labels, and doc layout the other skills assume. Custom issue trackers also work.

+ 5 - 0
.claude/skills/ask-matt/agents/openai.yaml

@@ -0,0 +1,5 @@
+interface:
+  display_name: "Ask Matt"
+  short_description: "Find the right skill or workflow"
+policy:
+  allow_implicit_invocation: false

+ 164 - 0
.claude/skills/brainstorming/SKILL.md

@@ -0,0 +1,164 @@
+---
+name: brainstorming
+description: "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation."
+---
+
+# Brainstorming Ideas Into Designs
+
+Help turn ideas into fully formed designs and specs through natural collaborative dialogue.
+
+Start by understanding the current project context, then ask questions one at a time to refine the idea. Once you understand what you're building, present the design and get user approval.
+
+<HARD-GATE>
+Do NOT invoke any implementation skill, write any code, scaffold any project, or take any implementation action until you have presented a design and the user has approved it. This applies to EVERY project regardless of perceived simplicity.
+</HARD-GATE>
+
+## Anti-Pattern: "This Is Too Simple To Need A Design"
+
+Every project goes through this process. A todo list, a single-function utility, a config change — all of them. "Simple" projects are where unexamined assumptions cause the most wasted work. The design can be short (a few sentences for truly simple projects), but you MUST present it and get approval.
+
+## Checklist
+
+You MUST create a task for each of these items and complete them in order:
+
+1. **Explore project context** — check files, docs, recent commits
+2. **Offer visual companion** (if topic will involve visual questions) — this is its own message, not combined with a clarifying question. See the Visual Companion section below.
+3. **Ask clarifying questions** — one at a time, understand purpose/constraints/success criteria
+4. **Propose 2-3 approaches** — with trade-offs and your recommendation
+5. **Present design** — in sections scaled to their complexity, get user approval after each section
+6. **Write design doc** — save to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` and commit
+7. **Spec self-review** — quick inline check for placeholders, contradictions, ambiguity, scope (see below)
+8. **User reviews written spec** — ask user to review the spec file before proceeding
+9. **Transition to implementation** — invoke writing-plans skill to create implementation plan
+
+## Process Flow
+
+```dot
+digraph brainstorming {
+    "Explore project context" [shape=box];
+    "Visual questions ahead?" [shape=diamond];
+    "Offer Visual Companion\n(own message, no other content)" [shape=box];
+    "Ask clarifying questions" [shape=box];
+    "Propose 2-3 approaches" [shape=box];
+    "Present design sections" [shape=box];
+    "User approves design?" [shape=diamond];
+    "Write design doc" [shape=box];
+    "Spec self-review\n(fix inline)" [shape=box];
+    "User reviews spec?" [shape=diamond];
+    "Invoke writing-plans skill" [shape=doublecircle];
+
+    "Explore project context" -> "Visual questions ahead?";
+    "Visual questions ahead?" -> "Offer Visual Companion\n(own message, no other content)" [label="yes"];
+    "Visual questions ahead?" -> "Ask clarifying questions" [label="no"];
+    "Offer Visual Companion\n(own message, no other content)" -> "Ask clarifying questions";
+    "Ask clarifying questions" -> "Propose 2-3 approaches";
+    "Propose 2-3 approaches" -> "Present design sections";
+    "Present design sections" -> "User approves design?";
+    "User approves design?" -> "Present design sections" [label="no, revise"];
+    "User approves design?" -> "Write design doc" [label="yes"];
+    "Write design doc" -> "Spec self-review\n(fix inline)";
+    "Spec self-review\n(fix inline)" -> "User reviews spec?";
+    "User reviews spec?" -> "Write design doc" [label="changes requested"];
+    "User reviews spec?" -> "Invoke writing-plans skill" [label="approved"];
+}
+```
+
+**The terminal state is invoking writing-plans.** Do NOT invoke frontend-design, mcp-builder, or any other implementation skill. The ONLY skill you invoke after brainstorming is writing-plans.
+
+## The Process
+
+**Understanding the idea:**
+
+- Check out the current project state first (files, docs, recent commits)
+- Before asking detailed questions, assess scope: if the request describes multiple independent subsystems (e.g., "build a platform with chat, file storage, billing, and analytics"), flag this immediately. Don't spend questions refining details of a project that needs to be decomposed first.
+- If the project is too large for a single spec, help the user decompose into sub-projects: what are the independent pieces, how do they relate, what order should they be built? Then brainstorm the first sub-project through the normal design flow. Each sub-project gets its own spec → plan → implementation cycle.
+- For appropriately-scoped projects, ask questions one at a time to refine the idea
+- Prefer multiple choice questions when possible, but open-ended is fine too
+- Only one question per message - if a topic needs more exploration, break it into multiple questions
+- Focus on understanding: purpose, constraints, success criteria
+
+**Exploring approaches:**
+
+- Propose 2-3 different approaches with trade-offs
+- Present options conversationally with your recommendation and reasoning
+- Lead with your recommended option and explain why
+
+**Presenting the design:**
+
+- Once you believe you understand what you're building, present the design
+- Scale each section to its complexity: a few sentences if straightforward, up to 200-300 words if nuanced
+- Ask after each section whether it looks right so far
+- Cover: architecture, components, data flow, error handling, testing
+- Be ready to go back and clarify if something doesn't make sense
+
+**Design for isolation and clarity:**
+
+- Break the system into smaller units that each have one clear purpose, communicate through well-defined interfaces, and can be understood and tested independently
+- For each unit, you should be able to answer: what does it do, how do you use it, and what does it depend on?
+- Can someone understand what a unit does without reading its internals? Can you change the internals without breaking consumers? If not, the boundaries need work.
+- Smaller, well-bounded units are also easier for you to work with - you reason better about code you can hold in context at once, and your edits are more reliable when files are focused. When a file grows large, that's often a signal that it's doing too much.
+
+**Working in existing codebases:**
+
+- Explore the current structure before proposing changes. Follow existing patterns.
+- Where existing code has problems that affect the work (e.g., a file that's grown too large, unclear boundaries, tangled responsibilities), include targeted improvements as part of the design - the way a good developer improves code they're working in.
+- Don't propose unrelated refactoring. Stay focused on what serves the current goal.
+
+## After the Design
+
+**Documentation:**
+
+- Write the validated design (spec) to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`
+  - (User preferences for spec location override this default)
+- Use elements-of-style:writing-clearly-and-concisely skill if available
+- Commit the design document to git
+
+**Spec Self-Review:**
+After writing the spec document, look at it with fresh eyes:
+
+1. **Placeholder scan:** Any "TBD", "TODO", incomplete sections, or vague requirements? Fix them.
+2. **Internal consistency:** Do any sections contradict each other? Does the architecture match the feature descriptions?
+3. **Scope check:** Is this focused enough for a single implementation plan, or does it need decomposition?
+4. **Ambiguity check:** Could any requirement be interpreted two different ways? If so, pick one and make it explicit.
+
+Fix any issues inline. No need to re-review — just fix and move on.
+
+**User Review Gate:**
+After the spec review loop passes, ask the user to review the written spec before proceeding:
+
+> "Spec written and committed to `<path>`. Please review it and let me know if you want to make any changes before we start writing out the implementation plan."
+
+Wait for the user's response. If they request changes, make them and re-run the spec review loop. Only proceed once the user approves.
+
+**Implementation:**
+
+- Invoke the writing-plans skill to create a detailed implementation plan
+- Do NOT invoke any other skill. writing-plans is the next step.
+
+## Key Principles
+
+- **One question at a time** - Don't overwhelm with multiple questions
+- **Multiple choice preferred** - Easier to answer than open-ended when possible
+- **YAGNI ruthlessly** - Remove unnecessary features from all designs
+- **Explore alternatives** - Always propose 2-3 approaches before settling
+- **Incremental validation** - Present design, get approval before moving on
+- **Be flexible** - Go back and clarify when something doesn't make sense
+
+## Visual Companion
+
+A browser-based companion for showing mockups, diagrams, and visual options during brainstorming. Available as a tool — not a mode. Accepting the companion means it's available for questions that benefit from visual treatment; it does NOT mean every question goes through the browser.
+
+**Offering the companion:** When you anticipate that upcoming questions will involve visual content (mockups, layouts, diagrams), offer it once for consent:
+> "Some of what we're working on might be easier to explain if I can show it to you in a web browser. I can put together mockups, diagrams, comparisons, and other visuals as we go. This feature is still new and can be token-intensive. Want to try it? (Requires opening a local URL)"
+
+**This offer MUST be its own message.** Do not combine it with clarifying questions, context summaries, or any other content. The message should contain ONLY the offer above and nothing else. Wait for the user's response before continuing. If they decline, proceed with text-only brainstorming.
+
+**Per-question decision:** Even after the user accepts, decide FOR EACH QUESTION whether to use the browser or the terminal. The test: **would the user understand this better by seeing it than reading it?**
+
+- **Use the browser** for content that IS visual — mockups, wireframes, layout comparisons, architecture diagrams, side-by-side visual designs
+- **Use the terminal** for content that is text — requirements questions, conceptual choices, tradeoff lists, A/B/C/D text options, scope decisions
+
+A question about a UI topic is not automatically a visual question. "What does personality mean in this context?" is a conceptual question — use the terminal. "Which wizard layout works better?" is a visual question — use the browser.
+
+If they agree to the companion, read the detailed guide before proceeding:
+`skills/brainstorming/visual-companion.md`

+ 214 - 0
.claude/skills/brainstorming/scripts/frame-template.html

@@ -0,0 +1,214 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Superpowers Brainstorming</title>
+  <style>
+    /*
+     * BRAINSTORM COMPANION FRAME TEMPLATE
+     *
+     * This template provides a consistent frame with:
+     * - OS-aware light/dark theming
+     * - Fixed header and selection indicator bar
+     * - Scrollable main content area
+     * - CSS helpers for common UI patterns
+     *
+     * Content is injected via placeholder comment in #claude-content.
+     */
+
+    * { box-sizing: border-box; margin: 0; padding: 0; }
+    html, body { height: 100%; overflow: hidden; }
+
+    /* ===== THEME VARIABLES ===== */
+    :root {
+      --bg-primary: #f5f5f7;
+      --bg-secondary: #ffffff;
+      --bg-tertiary: #e5e5e7;
+      --border: #d1d1d6;
+      --text-primary: #1d1d1f;
+      --text-secondary: #86868b;
+      --text-tertiary: #aeaeb2;
+      --accent: #0071e3;
+      --accent-hover: #0077ed;
+      --success: #34c759;
+      --warning: #ff9f0a;
+      --error: #ff3b30;
+      --selected-bg: #e8f4fd;
+      --selected-border: #0071e3;
+    }
+
+    @media (prefers-color-scheme: dark) {
+      :root {
+        --bg-primary: #1d1d1f;
+        --bg-secondary: #2d2d2f;
+        --bg-tertiary: #3d3d3f;
+        --border: #424245;
+        --text-primary: #f5f5f7;
+        --text-secondary: #86868b;
+        --text-tertiary: #636366;
+        --accent: #0a84ff;
+        --accent-hover: #409cff;
+        --selected-bg: rgba(10, 132, 255, 0.15);
+        --selected-border: #0a84ff;
+      }
+    }
+
+    body {
+      font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
+      background: var(--bg-primary);
+      color: var(--text-primary);
+      display: flex;
+      flex-direction: column;
+      line-height: 1.5;
+    }
+
+    /* ===== FRAME STRUCTURE ===== */
+    .header {
+      background: var(--bg-secondary);
+      padding: 0.5rem 1.5rem;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      border-bottom: 1px solid var(--border);
+      flex-shrink: 0;
+    }
+    .header h1 { font-size: 0.85rem; font-weight: 500; color: var(--text-secondary); }
+    .header .status { font-size: 0.7rem; color: var(--success); display: flex; align-items: center; gap: 0.4rem; }
+    .header .status::before { content: ''; width: 6px; height: 6px; background: var(--success); border-radius: 50%; }
+
+    .main { flex: 1; overflow-y: auto; }
+    #claude-content { padding: 2rem; min-height: 100%; }
+
+    .indicator-bar {
+      background: var(--bg-secondary);
+      border-top: 1px solid var(--border);
+      padding: 0.5rem 1.5rem;
+      flex-shrink: 0;
+      text-align: center;
+    }
+    .indicator-bar span {
+      font-size: 0.75rem;
+      color: var(--text-secondary);
+    }
+    .indicator-bar .selected-text {
+      color: var(--accent);
+      font-weight: 500;
+    }
+
+    /* ===== TYPOGRAPHY ===== */
+    h2 { font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem; }
+    h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.25rem; }
+    .subtitle { color: var(--text-secondary); margin-bottom: 1.5rem; }
+    .section { margin-bottom: 2rem; }
+    .label { font-size: 0.7rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
+
+    /* ===== OPTIONS (for A/B/C choices) ===== */
+    .options { display: flex; flex-direction: column; gap: 0.75rem; }
+    .option {
+      background: var(--bg-secondary);
+      border: 2px solid var(--border);
+      border-radius: 12px;
+      padding: 1rem 1.25rem;
+      cursor: pointer;
+      transition: all 0.15s ease;
+      display: flex;
+      align-items: flex-start;
+      gap: 1rem;
+    }
+    .option:hover { border-color: var(--accent); }
+    .option.selected { background: var(--selected-bg); border-color: var(--selected-border); }
+    .option .letter {
+      background: var(--bg-tertiary);
+      color: var(--text-secondary);
+      width: 1.75rem; height: 1.75rem;
+      border-radius: 6px;
+      display: flex; align-items: center; justify-content: center;
+      font-weight: 600; font-size: 0.85rem; flex-shrink: 0;
+    }
+    .option.selected .letter { background: var(--accent); color: white; }
+    .option .content { flex: 1; }
+    .option .content h3 { font-size: 0.95rem; margin-bottom: 0.15rem; }
+    .option .content p { color: var(--text-secondary); font-size: 0.85rem; margin: 0; }
+
+    /* ===== CARDS (for showing designs/mockups) ===== */
+    .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; }
+    .card {
+      background: var(--bg-secondary);
+      border: 1px solid var(--border);
+      border-radius: 12px;
+      overflow: hidden;
+      cursor: pointer;
+      transition: all 0.15s ease;
+    }
+    .card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
+    .card.selected { border-color: var(--selected-border); border-width: 2px; }
+    .card-image { background: var(--bg-tertiary); aspect-ratio: 16/10; display: flex; align-items: center; justify-content: center; }
+    .card-body { padding: 1rem; }
+    .card-body h3 { margin-bottom: 0.25rem; }
+    .card-body p { color: var(--text-secondary); font-size: 0.85rem; }
+
+    /* ===== MOCKUP CONTAINER ===== */
+    .mockup {
+      background: var(--bg-secondary);
+      border: 1px solid var(--border);
+      border-radius: 12px;
+      overflow: hidden;
+      margin-bottom: 1.5rem;
+    }
+    .mockup-header {
+      background: var(--bg-tertiary);
+      padding: 0.5rem 1rem;
+      font-size: 0.75rem;
+      color: var(--text-secondary);
+      border-bottom: 1px solid var(--border);
+    }
+    .mockup-body { padding: 1.5rem; }
+
+    /* ===== SPLIT VIEW (side-by-side comparison) ===== */
+    .split { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
+    @media (max-width: 700px) { .split { grid-template-columns: 1fr; } }
+
+    /* ===== PROS/CONS ===== */
+    .pros-cons { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1rem 0; }
+    .pros, .cons { background: var(--bg-secondary); border-radius: 8px; padding: 1rem; }
+    .pros h4 { color: var(--success); font-size: 0.85rem; margin-bottom: 0.5rem; }
+    .cons h4 { color: var(--error); font-size: 0.85rem; margin-bottom: 0.5rem; }
+    .pros ul, .cons ul { margin-left: 1.25rem; font-size: 0.85rem; color: var(--text-secondary); }
+    .pros li, .cons li { margin-bottom: 0.25rem; }
+
+    /* ===== PLACEHOLDER (for mockup areas) ===== */
+    .placeholder {
+      background: var(--bg-tertiary);
+      border: 2px dashed var(--border);
+      border-radius: 8px;
+      padding: 2rem;
+      text-align: center;
+      color: var(--text-tertiary);
+    }
+
+    /* ===== INLINE MOCKUP ELEMENTS ===== */
+    .mock-nav { background: var(--accent); color: white; padding: 0.75rem 1rem; display: flex; gap: 1.5rem; font-size: 0.9rem; }
+    .mock-sidebar { background: var(--bg-tertiary); padding: 1rem; min-width: 180px; }
+    .mock-content { padding: 1.5rem; flex: 1; }
+    .mock-button { background: var(--accent); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.85rem; }
+    .mock-input { background: var(--bg-primary); border: 1px solid var(--border); border-radius: 6px; padding: 0.5rem; width: 100%; }
+  </style>
+</head>
+<body>
+  <div class="header">
+    <h1><a href="https://github.com/obra/superpowers" style="color: inherit; text-decoration: none;">Superpowers Brainstorming</a></h1>
+    <div class="status">Connected</div>
+  </div>
+
+  <div class="main">
+    <div id="claude-content">
+      <!-- CONTENT -->
+    </div>
+  </div>
+
+  <div class="indicator-bar">
+    <span id="indicator-text">Click an option above, then return to the terminal</span>
+  </div>
+
+</body>
+</html>

+ 88 - 0
.claude/skills/brainstorming/scripts/helper.js

@@ -0,0 +1,88 @@
+(function() {
+  const WS_URL = 'ws://' + window.location.host;
+  let ws = null;
+  let eventQueue = [];
+
+  function connect() {
+    ws = new WebSocket(WS_URL);
+
+    ws.onopen = () => {
+      eventQueue.forEach(e => ws.send(JSON.stringify(e)));
+      eventQueue = [];
+    };
+
+    ws.onmessage = (msg) => {
+      const data = JSON.parse(msg.data);
+      if (data.type === 'reload') {
+        window.location.reload();
+      }
+    };
+
+    ws.onclose = () => {
+      setTimeout(connect, 1000);
+    };
+  }
+
+  function sendEvent(event) {
+    event.timestamp = Date.now();
+    if (ws && ws.readyState === WebSocket.OPEN) {
+      ws.send(JSON.stringify(event));
+    } else {
+      eventQueue.push(event);
+    }
+  }
+
+  // Capture clicks on choice elements
+  document.addEventListener('click', (e) => {
+    const target = e.target.closest('[data-choice]');
+    if (!target) return;
+
+    sendEvent({
+      type: 'click',
+      text: target.textContent.trim(),
+      choice: target.dataset.choice,
+      id: target.id || null
+    });
+
+    // Update indicator bar (defer so toggleSelect runs first)
+    setTimeout(() => {
+      const indicator = document.getElementById('indicator-text');
+      if (!indicator) return;
+      const container = target.closest('.options') || target.closest('.cards');
+      const selected = container ? container.querySelectorAll('.selected') : [];
+      if (selected.length === 0) {
+        indicator.textContent = 'Click an option above, then return to the terminal';
+      } else if (selected.length === 1) {
+        const label = selected[0].querySelector('h3, .content h3, .card-body h3')?.textContent?.trim() || selected[0].dataset.choice;
+        indicator.innerHTML = '<span class="selected-text">' + label + ' selected</span> — return to terminal to continue';
+      } else {
+        indicator.innerHTML = '<span class="selected-text">' + selected.length + ' selected</span> — return to terminal to continue';
+      }
+    }, 0);
+  });
+
+  // Frame UI: selection tracking
+  window.selectedChoice = null;
+
+  window.toggleSelect = function(el) {
+    const container = el.closest('.options') || el.closest('.cards');
+    const multi = container && container.dataset.multiselect !== undefined;
+    if (container && !multi) {
+      container.querySelectorAll('.option, .card').forEach(o => o.classList.remove('selected'));
+    }
+    if (multi) {
+      el.classList.toggle('selected');
+    } else {
+      el.classList.add('selected');
+    }
+    window.selectedChoice = el.dataset.choice;
+  };
+
+  // Expose API for explicit use
+  window.brainstorm = {
+    send: sendEvent,
+    choice: (value, metadata = {}) => sendEvent({ type: 'choice', value, ...metadata })
+  };
+
+  connect();
+})();

+ 354 - 0
.claude/skills/brainstorming/scripts/server.cjs

@@ -0,0 +1,354 @@
+const crypto = require('crypto');
+const http = require('http');
+const fs = require('fs');
+const path = require('path');
+
+// ========== WebSocket Protocol (RFC 6455) ==========
+
+const OPCODES = { TEXT: 0x01, CLOSE: 0x08, PING: 0x09, PONG: 0x0A };
+const WS_MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
+
+function computeAcceptKey(clientKey) {
+  return crypto.createHash('sha1').update(clientKey + WS_MAGIC).digest('base64');
+}
+
+function encodeFrame(opcode, payload) {
+  const fin = 0x80;
+  const len = payload.length;
+  let header;
+
+  if (len < 126) {
+    header = Buffer.alloc(2);
+    header[0] = fin | opcode;
+    header[1] = len;
+  } else if (len < 65536) {
+    header = Buffer.alloc(4);
+    header[0] = fin | opcode;
+    header[1] = 126;
+    header.writeUInt16BE(len, 2);
+  } else {
+    header = Buffer.alloc(10);
+    header[0] = fin | opcode;
+    header[1] = 127;
+    header.writeBigUInt64BE(BigInt(len), 2);
+  }
+
+  return Buffer.concat([header, payload]);
+}
+
+function decodeFrame(buffer) {
+  if (buffer.length < 2) return null;
+
+  const secondByte = buffer[1];
+  const opcode = buffer[0] & 0x0F;
+  const masked = (secondByte & 0x80) !== 0;
+  let payloadLen = secondByte & 0x7F;
+  let offset = 2;
+
+  if (!masked) throw new Error('Client frames must be masked');
+
+  if (payloadLen === 126) {
+    if (buffer.length < 4) return null;
+    payloadLen = buffer.readUInt16BE(2);
+    offset = 4;
+  } else if (payloadLen === 127) {
+    if (buffer.length < 10) return null;
+    payloadLen = Number(buffer.readBigUInt64BE(2));
+    offset = 10;
+  }
+
+  const maskOffset = offset;
+  const dataOffset = offset + 4;
+  const totalLen = dataOffset + payloadLen;
+  if (buffer.length < totalLen) return null;
+
+  const mask = buffer.slice(maskOffset, dataOffset);
+  const data = Buffer.alloc(payloadLen);
+  for (let i = 0; i < payloadLen; i++) {
+    data[i] = buffer[dataOffset + i] ^ mask[i % 4];
+  }
+
+  return { opcode, payload: data, bytesConsumed: totalLen };
+}
+
+// ========== Configuration ==========
+
+const PORT = process.env.BRAINSTORM_PORT || (49152 + Math.floor(Math.random() * 16383));
+const HOST = process.env.BRAINSTORM_HOST || '127.0.0.1';
+const URL_HOST = process.env.BRAINSTORM_URL_HOST || (HOST === '127.0.0.1' ? 'localhost' : HOST);
+const SESSION_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm';
+const CONTENT_DIR = path.join(SESSION_DIR, 'content');
+const STATE_DIR = path.join(SESSION_DIR, 'state');
+let ownerPid = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null;
+
+const MIME_TYPES = {
+  '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript',
+  '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg',
+  '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml'
+};
+
+// ========== Templates and Constants ==========
+
+const WAITING_PAGE = `<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"><title>Brainstorm Companion</title>
+<style>body { font-family: system-ui, sans-serif; padding: 2rem; max-width: 800px; margin: 0 auto; }
+h1 { color: #333; } p { color: #666; }</style>
+</head>
+<body><h1>Brainstorm Companion</h1>
+<p>Waiting for the agent to push a screen...</p></body></html>`;
+
+const frameTemplate = fs.readFileSync(path.join(__dirname, 'frame-template.html'), 'utf-8');
+const helperScript = fs.readFileSync(path.join(__dirname, 'helper.js'), 'utf-8');
+const helperInjection = '<script>\n' + helperScript + '\n</script>';
+
+// ========== Helper Functions ==========
+
+function isFullDocument(html) {
+  const trimmed = html.trimStart().toLowerCase();
+  return trimmed.startsWith('<!doctype') || trimmed.startsWith('<html');
+}
+
+function wrapInFrame(content) {
+  return frameTemplate.replace('<!-- CONTENT -->', content);
+}
+
+function getNewestScreen() {
+  const files = fs.readdirSync(CONTENT_DIR)
+    .filter(f => f.endsWith('.html'))
+    .map(f => {
+      const fp = path.join(CONTENT_DIR, f);
+      return { path: fp, mtime: fs.statSync(fp).mtime.getTime() };
+    })
+    .sort((a, b) => b.mtime - a.mtime);
+  return files.length > 0 ? files[0].path : null;
+}
+
+// ========== HTTP Request Handler ==========
+
+function handleRequest(req, res) {
+  touchActivity();
+  if (req.method === 'GET' && req.url === '/') {
+    const screenFile = getNewestScreen();
+    let html = screenFile
+      ? (raw => isFullDocument(raw) ? raw : wrapInFrame(raw))(fs.readFileSync(screenFile, 'utf-8'))
+      : WAITING_PAGE;
+
+    if (html.includes('</body>')) {
+      html = html.replace('</body>', helperInjection + '\n</body>');
+    } else {
+      html += helperInjection;
+    }
+
+    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+    res.end(html);
+  } else if (req.method === 'GET' && req.url.startsWith('/files/')) {
+    const fileName = req.url.slice(7);
+    const filePath = path.join(CONTENT_DIR, path.basename(fileName));
+    if (!fs.existsSync(filePath)) {
+      res.writeHead(404);
+      res.end('Not found');
+      return;
+    }
+    const ext = path.extname(filePath).toLowerCase();
+    const contentType = MIME_TYPES[ext] || 'application/octet-stream';
+    res.writeHead(200, { 'Content-Type': contentType });
+    res.end(fs.readFileSync(filePath));
+  } else {
+    res.writeHead(404);
+    res.end('Not found');
+  }
+}
+
+// ========== WebSocket Connection Handling ==========
+
+const clients = new Set();
+
+function handleUpgrade(req, socket) {
+  const key = req.headers['sec-websocket-key'];
+  if (!key) { socket.destroy(); return; }
+
+  const accept = computeAcceptKey(key);
+  socket.write(
+    'HTTP/1.1 101 Switching Protocols\r\n' +
+    'Upgrade: websocket\r\n' +
+    'Connection: Upgrade\r\n' +
+    'Sec-WebSocket-Accept: ' + accept + '\r\n\r\n'
+  );
+
+  let buffer = Buffer.alloc(0);
+  clients.add(socket);
+
+  socket.on('data', (chunk) => {
+    buffer = Buffer.concat([buffer, chunk]);
+    while (buffer.length > 0) {
+      let result;
+      try {
+        result = decodeFrame(buffer);
+      } catch (e) {
+        socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0)));
+        clients.delete(socket);
+        return;
+      }
+      if (!result) break;
+      buffer = buffer.slice(result.bytesConsumed);
+
+      switch (result.opcode) {
+        case OPCODES.TEXT:
+          handleMessage(result.payload.toString());
+          break;
+        case OPCODES.CLOSE:
+          socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0)));
+          clients.delete(socket);
+          return;
+        case OPCODES.PING:
+          socket.write(encodeFrame(OPCODES.PONG, result.payload));
+          break;
+        case OPCODES.PONG:
+          break;
+        default: {
+          const closeBuf = Buffer.alloc(2);
+          closeBuf.writeUInt16BE(1003);
+          socket.end(encodeFrame(OPCODES.CLOSE, closeBuf));
+          clients.delete(socket);
+          return;
+        }
+      }
+    }
+  });
+
+  socket.on('close', () => clients.delete(socket));
+  socket.on('error', () => clients.delete(socket));
+}
+
+function handleMessage(text) {
+  let event;
+  try {
+    event = JSON.parse(text);
+  } catch (e) {
+    console.error('Failed to parse WebSocket message:', e.message);
+    return;
+  }
+  touchActivity();
+  console.log(JSON.stringify({ source: 'user-event', ...event }));
+  if (event.choice) {
+    const eventsFile = path.join(STATE_DIR, 'events');
+    fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n');
+  }
+}
+
+function broadcast(msg) {
+  const frame = encodeFrame(OPCODES.TEXT, Buffer.from(JSON.stringify(msg)));
+  for (const socket of clients) {
+    try { socket.write(frame); } catch (e) { clients.delete(socket); }
+  }
+}
+
+// ========== Activity Tracking ==========
+
+const IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
+let lastActivity = Date.now();
+
+function touchActivity() {
+  lastActivity = Date.now();
+}
+
+// ========== File Watching ==========
+
+const debounceTimers = new Map();
+
+// ========== Server Startup ==========
+
+function startServer() {
+  if (!fs.existsSync(CONTENT_DIR)) fs.mkdirSync(CONTENT_DIR, { recursive: true });
+  if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
+
+  // Track known files to distinguish new screens from updates.
+  // macOS fs.watch reports 'rename' for both new files and overwrites,
+  // so we can't rely on eventType alone.
+  const knownFiles = new Set(
+    fs.readdirSync(CONTENT_DIR).filter(f => f.endsWith('.html'))
+  );
+
+  const server = http.createServer(handleRequest);
+  server.on('upgrade', handleUpgrade);
+
+  const watcher = fs.watch(CONTENT_DIR, (eventType, filename) => {
+    if (!filename || !filename.endsWith('.html')) return;
+
+    if (debounceTimers.has(filename)) clearTimeout(debounceTimers.get(filename));
+    debounceTimers.set(filename, setTimeout(() => {
+      debounceTimers.delete(filename);
+      const filePath = path.join(CONTENT_DIR, filename);
+
+      if (!fs.existsSync(filePath)) return; // file was deleted
+      touchActivity();
+
+      if (!knownFiles.has(filename)) {
+        knownFiles.add(filename);
+        const eventsFile = path.join(STATE_DIR, 'events');
+        if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
+        console.log(JSON.stringify({ type: 'screen-added', file: filePath }));
+      } else {
+        console.log(JSON.stringify({ type: 'screen-updated', file: filePath }));
+      }
+
+      broadcast({ type: 'reload' });
+    }, 100));
+  });
+  watcher.on('error', (err) => console.error('fs.watch error:', err.message));
+
+  function shutdown(reason) {
+    console.log(JSON.stringify({ type: 'server-stopped', reason }));
+    const infoFile = path.join(STATE_DIR, 'server-info');
+    if (fs.existsSync(infoFile)) fs.unlinkSync(infoFile);
+    fs.writeFileSync(
+      path.join(STATE_DIR, 'server-stopped'),
+      JSON.stringify({ reason, timestamp: Date.now() }) + '\n'
+    );
+    watcher.close();
+    clearInterval(lifecycleCheck);
+    server.close(() => process.exit(0));
+  }
+
+  function ownerAlive() {
+    if (!ownerPid) return true;
+    try { process.kill(ownerPid, 0); return true; } catch (e) { return e.code === 'EPERM'; }
+  }
+
+  // Check every 60s: exit if owner process died or idle for 30 minutes
+  const lifecycleCheck = setInterval(() => {
+    if (!ownerAlive()) shutdown('owner process exited');
+    else if (Date.now() - lastActivity > IDLE_TIMEOUT_MS) shutdown('idle timeout');
+  }, 60 * 1000);
+  lifecycleCheck.unref();
+
+  // Validate owner PID at startup. If it's already dead, the PID resolution
+  // was wrong (common on WSL, Tailscale SSH, and cross-user scenarios).
+  // Disable monitoring and rely on the idle timeout instead.
+  if (ownerPid) {
+    try { process.kill(ownerPid, 0); }
+    catch (e) {
+      if (e.code !== 'EPERM') {
+        console.log(JSON.stringify({ type: 'owner-pid-invalid', pid: ownerPid, reason: 'dead at startup' }));
+        ownerPid = null;
+      }
+    }
+  }
+
+  server.listen(PORT, HOST, () => {
+    const info = JSON.stringify({
+      type: 'server-started', port: Number(PORT), host: HOST,
+      url_host: URL_HOST, url: 'http://' + URL_HOST + ':' + PORT,
+      screen_dir: CONTENT_DIR, state_dir: STATE_DIR
+    });
+    console.log(info);
+    fs.writeFileSync(path.join(STATE_DIR, 'server-info'), info + '\n');
+  });
+}
+
+if (require.main === module) {
+  startServer();
+}
+
+module.exports = { computeAcceptKey, encodeFrame, decodeFrame, OPCODES };

+ 148 - 0
.claude/skills/brainstorming/scripts/start-server.sh

@@ -0,0 +1,148 @@
+#!/usr/bin/env bash
+# Start the brainstorm server and output connection info
+# Usage: start-server.sh [--project-dir <path>] [--host <bind-host>] [--url-host <display-host>] [--foreground] [--background]
+#
+# Starts server on a random high port, outputs JSON with URL.
+# Each session gets its own directory to avoid conflicts.
+#
+# Options:
+#   --project-dir <path>  Store session files under <path>/.superpowers/brainstorm/
+#                         instead of /tmp. Files persist after server stops.
+#   --host <bind-host>    Host/interface to bind (default: 127.0.0.1).
+#                         Use 0.0.0.0 in remote/containerized environments.
+#   --url-host <host>     Hostname shown in returned URL JSON.
+#   --foreground          Run server in the current terminal (no backgrounding).
+#   --background          Force background mode (overrides Codex auto-foreground).
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+# Parse arguments
+PROJECT_DIR=""
+FOREGROUND="false"
+FORCE_BACKGROUND="false"
+BIND_HOST="127.0.0.1"
+URL_HOST=""
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    --project-dir)
+      PROJECT_DIR="$2"
+      shift 2
+      ;;
+    --host)
+      BIND_HOST="$2"
+      shift 2
+      ;;
+    --url-host)
+      URL_HOST="$2"
+      shift 2
+      ;;
+    --foreground|--no-daemon)
+      FOREGROUND="true"
+      shift
+      ;;
+    --background|--daemon)
+      FORCE_BACKGROUND="true"
+      shift
+      ;;
+    *)
+      echo "{\"error\": \"Unknown argument: $1\"}"
+      exit 1
+      ;;
+  esac
+done
+
+if [[ -z "$URL_HOST" ]]; then
+  if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
+    URL_HOST="localhost"
+  else
+    URL_HOST="$BIND_HOST"
+  fi
+fi
+
+# Some environments reap detached/background processes. Auto-foreground when detected.
+if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
+  FOREGROUND="true"
+fi
+
+# Windows/Git Bash reaps nohup background processes. Auto-foreground when detected.
+if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
+  case "${OSTYPE:-}" in
+    msys*|cygwin*|mingw*) FOREGROUND="true" ;;
+  esac
+  if [[ -n "${MSYSTEM:-}" ]]; then
+    FOREGROUND="true"
+  fi
+fi
+
+# Generate unique session directory
+SESSION_ID="$$-$(date +%s)"
+
+if [[ -n "$PROJECT_DIR" ]]; then
+  SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
+else
+  SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
+fi
+
+STATE_DIR="${SESSION_DIR}/state"
+PID_FILE="${STATE_DIR}/server.pid"
+LOG_FILE="${STATE_DIR}/server.log"
+
+# Create fresh session directory with content and state peers
+mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
+
+# Kill any existing server
+if [[ -f "$PID_FILE" ]]; then
+  old_pid=$(cat "$PID_FILE")
+  kill "$old_pid" 2>/dev/null
+  rm -f "$PID_FILE"
+fi
+
+cd "$SCRIPT_DIR"
+
+# Resolve the harness PID (grandparent of this script).
+# $PPID is the ephemeral shell the harness spawned to run us — it dies
+# when this script exits. The harness itself is $PPID's parent.
+OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')"
+if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
+  OWNER_PID="$PPID"
+fi
+
+# Foreground mode for environments that reap detached/background processes.
+if [[ "$FOREGROUND" == "true" ]]; then
+  echo "$$" > "$PID_FILE"
+  env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs
+  exit $?
+fi
+
+# Start server, capturing output to log file
+# Use nohup to survive shell exit; disown to remove from job table
+nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs > "$LOG_FILE" 2>&1 &
+SERVER_PID=$!
+disown "$SERVER_PID" 2>/dev/null
+echo "$SERVER_PID" > "$PID_FILE"
+
+# Wait for server-started message (check log file)
+for i in {1..50}; do
+  if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
+    # Verify server is still alive after a short window (catches process reapers)
+    alive="true"
+    for _ in {1..20}; do
+      if ! kill -0 "$SERVER_PID" 2>/dev/null; then
+        alive="false"
+        break
+      fi
+      sleep 0.1
+    done
+    if [[ "$alive" != "true" ]]; then
+      echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}"
+      exit 1
+    fi
+    grep "server-started" "$LOG_FILE" | head -1
+    exit 0
+  fi
+  sleep 0.1
+done
+
+# Timeout - server didn't start
+echo '{"error": "Server failed to start within 5 seconds"}'
+exit 1

+ 56 - 0
.claude/skills/brainstorming/scripts/stop-server.sh

@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# Stop the brainstorm server and clean up
+# Usage: stop-server.sh <session_dir>
+#
+# Kills the server process. Only deletes session directory if it's
+# under /tmp (ephemeral). Persistent directories (.superpowers/) are
+# kept so mockups can be reviewed later.
+
+SESSION_DIR="$1"
+
+if [[ -z "$SESSION_DIR" ]]; then
+  echo '{"error": "Usage: stop-server.sh <session_dir>"}'
+  exit 1
+fi
+
+STATE_DIR="${SESSION_DIR}/state"
+PID_FILE="${STATE_DIR}/server.pid"
+
+if [[ -f "$PID_FILE" ]]; then
+  pid=$(cat "$PID_FILE")
+
+  # Try to stop gracefully, fallback to force if still alive
+  kill "$pid" 2>/dev/null || true
+
+  # Wait for graceful shutdown (up to ~2s)
+  for i in {1..20}; do
+    if ! kill -0 "$pid" 2>/dev/null; then
+      break
+    fi
+    sleep 0.1
+  done
+
+  # If still running, escalate to SIGKILL
+  if kill -0 "$pid" 2>/dev/null; then
+    kill -9 "$pid" 2>/dev/null || true
+
+    # Give SIGKILL a moment to take effect
+    sleep 0.1
+  fi
+
+  if kill -0 "$pid" 2>/dev/null; then
+    echo '{"status": "failed", "error": "process still running"}'
+    exit 1
+  fi
+
+  rm -f "$PID_FILE" "${STATE_DIR}/server.log"
+
+  # Only delete ephemeral /tmp directories
+  if [[ "$SESSION_DIR" == /tmp/* ]]; then
+    rm -rf "$SESSION_DIR"
+  fi
+
+  echo '{"status": "stopped"}'
+else
+  echo '{"status": "not_running"}'
+fi

+ 49 - 0
.claude/skills/brainstorming/spec-document-reviewer-prompt.md

@@ -0,0 +1,49 @@
+# Spec Document Reviewer Prompt Template
+
+Use this template when dispatching a spec document reviewer subagent.
+
+**Purpose:** Verify the spec is complete, consistent, and ready for implementation planning.
+
+**Dispatch after:** Spec document is written to docs/superpowers/specs/
+
+```
+Task tool (general-purpose):
+  description: "Review spec document"
+  prompt: |
+    You are a spec document reviewer. Verify this spec is complete and ready for planning.
+
+    **Spec to review:** [SPEC_FILE_PATH]
+
+    ## What to Check
+
+    | Category | What to Look For |
+    |----------|------------------|
+    | Completeness | TODOs, placeholders, "TBD", incomplete sections |
+    | Consistency | Internal contradictions, conflicting requirements |
+    | Clarity | Requirements ambiguous enough to cause someone to build the wrong thing |
+    | Scope | Focused enough for a single plan — not covering multiple independent subsystems |
+    | YAGNI | Unrequested features, over-engineering |
+
+    ## Calibration
+
+    **Only flag issues that would cause real problems during implementation planning.**
+    A missing section, a contradiction, or a requirement so ambiguous it could be
+    interpreted two different ways — those are issues. Minor wording improvements,
+    stylistic preferences, and "sections less detailed than others" are not.
+
+    Approve unless there are serious gaps that would lead to a flawed plan.
+
+    ## Output Format
+
+    ## Spec Review
+
+    **Status:** Approved | Issues Found
+
+    **Issues (if any):**
+    - [Section X]: [specific issue] - [why it matters for planning]
+
+    **Recommendations (advisory, do not block approval):**
+    - [suggestions for improvement]
+```
+
+**Reviewer returns:** Status, Issues (if any), Recommendations

+ 287 - 0
.claude/skills/brainstorming/visual-companion.md

@@ -0,0 +1,287 @@
+# Visual Companion Guide
+
+Browser-based visual brainstorming companion for showing mockups, diagrams, and options.
+
+## When to Use
+
+Decide per-question, not per-session. The test: **would the user understand this better by seeing it than reading it?**
+
+**Use the browser** when the content itself is visual:
+
+- **UI mockups** — wireframes, layouts, navigation structures, component designs
+- **Architecture diagrams** — system components, data flow, relationship maps
+- **Side-by-side visual comparisons** — comparing two layouts, two color schemes, two design directions
+- **Design polish** — when the question is about look and feel, spacing, visual hierarchy
+- **Spatial relationships** — state machines, flowcharts, entity relationships rendered as diagrams
+
+**Use the terminal** when the content is text or tabular:
+
+- **Requirements and scope questions** — "what does X mean?", "which features are in scope?"
+- **Conceptual A/B/C choices** — picking between approaches described in words
+- **Tradeoff lists** — pros/cons, comparison tables
+- **Technical decisions** — API design, data modeling, architectural approach selection
+- **Clarifying questions** — anything where the answer is words, not a visual preference
+
+A question *about* a UI topic is not automatically a visual question. "What kind of wizard do you want?" is conceptual — use the terminal. "Which of these wizard layouts feels right?" is visual — use the browser.
+
+## How It Works
+
+The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content to `screen_dir`, the user sees it in their browser and can click to select options. Selections are recorded to `state_dir/events` that you read on your next turn.
+
+**Content fragments vs full documents:** If your HTML file starts with `<!DOCTYPE` or `<html`, the server serves it as-is (just injects the helper script). Otherwise, the server automatically wraps your content in the frame template — adding the header, CSS theme, selection indicator, and all interactive infrastructure. **Write content fragments by default.** Only write full documents when you need complete control over the page.
+
+## Starting a Session
+
+```bash
+# Start server with persistence (mockups saved to project)
+scripts/start-server.sh --project-dir /path/to/project
+
+# Returns: {"type":"server-started","port":52341,"url":"http://localhost:52341",
+#           "screen_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/content",
+#           "state_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/state"}
+```
+
+Save `screen_dir` and `state_dir` from the response. Tell user to open the URL.
+
+**Finding connection info:** The server writes its startup JSON to `$STATE_DIR/server-info`. If you launched the server in the background and didn't capture stdout, read that file to get the URL and port. When using `--project-dir`, check `<project>/.superpowers/brainstorm/` for the session directory.
+
+**Note:** Pass the project root as `--project-dir` so mockups persist in `.superpowers/brainstorm/` and survive server restarts. Without it, files go to `/tmp` and get cleaned up. Remind the user to add `.superpowers/` to `.gitignore` if it's not already there.
+
+**Launching the server by platform:**
+
+**Claude Code (macOS / Linux):**
+```bash
+# Default mode works — the script backgrounds the server itself
+scripts/start-server.sh --project-dir /path/to/project
+```
+
+**Claude Code (Windows):**
+```bash
+# Windows auto-detects and uses foreground mode, which blocks the tool call.
+# Use run_in_background: true on the Bash tool call so the server survives
+# across conversation turns.
+scripts/start-server.sh --project-dir /path/to/project
+```
+When calling this via the Bash tool, set `run_in_background: true`. Then read `$STATE_DIR/server-info` on the next turn to get the URL and port.
+
+**Codex:**
+```bash
+# Codex reaps background processes. The script auto-detects CODEX_CI and
+# switches to foreground mode. Run it normally — no extra flags needed.
+scripts/start-server.sh --project-dir /path/to/project
+```
+
+**Gemini CLI:**
+```bash
+# Use --foreground and set is_background: true on your shell tool call
+# so the process survives across turns
+scripts/start-server.sh --project-dir /path/to/project --foreground
+```
+
+**Other environments:** The server must keep running in the background across conversation turns. If your environment reaps detached processes, use `--foreground` and launch the command with your platform's background execution mechanism.
+
+If the URL is unreachable from your browser (common in remote/containerized setups), bind a non-loopback host:
+
+```bash
+scripts/start-server.sh \
+  --project-dir /path/to/project \
+  --host 0.0.0.0 \
+  --url-host localhost
+```
+
+Use `--url-host` to control what hostname is printed in the returned URL JSON.
+
+## The Loop
+
+1. **Check server is alive**, then **write HTML** to a new file in `screen_dir`:
+   - Before each write, check that `$STATE_DIR/server-info` exists. If it doesn't (or `$STATE_DIR/server-stopped` exists), the server has shut down — restart it with `start-server.sh` before continuing. The server auto-exits after 30 minutes of inactivity.
+   - Use semantic filenames: `platform.html`, `visual-style.html`, `layout.html`
+   - **Never reuse filenames** — each screen gets a fresh file
+   - Use Write tool — **never use cat/heredoc** (dumps noise into terminal)
+   - Server automatically serves the newest file
+
+2. **Tell user what to expect and end your turn:**
+   - Remind them of the URL (every step, not just first)
+   - Give a brief text summary of what's on screen (e.g., "Showing 3 layout options for the homepage")
+   - Ask them to respond in the terminal: "Take a look and let me know what you think. Click to select an option if you'd like."
+
+3. **On your next turn** — after the user responds in the terminal:
+   - Read `$STATE_DIR/events` if it exists — this contains the user's browser interactions (clicks, selections) as JSON lines
+   - Merge with the user's terminal text to get the full picture
+   - The terminal message is the primary feedback; `state_dir/events` provides structured interaction data
+
+4. **Iterate or advance** — if feedback changes current screen, write a new file (e.g., `layout-v2.html`). Only move to the next question when the current step is validated.
+
+5. **Unload when returning to terminal** — when the next step doesn't need the browser (e.g., a clarifying question, a tradeoff discussion), push a waiting screen to clear the stale content:
+
+   ```html
+   <!-- filename: waiting.html (or waiting-2.html, etc.) -->
+   <div style="display:flex;align-items:center;justify-content:center;min-height:60vh">
+     <p class="subtitle">Continuing in terminal...</p>
+   </div>
+   ```
+
+   This prevents the user from staring at a resolved choice while the conversation has moved on. When the next visual question comes up, push a new content file as usual.
+
+6. Repeat until done.
+
+## Writing Content Fragments
+
+Write just the content that goes inside the page. The server wraps it in the frame template automatically (header, theme CSS, selection indicator, and all interactive infrastructure).
+
+**Minimal example:**
+
+```html
+<h2>Which layout works better?</h2>
+<p class="subtitle">Consider readability and visual hierarchy</p>
+
+<div class="options">
+  <div class="option" data-choice="a" onclick="toggleSelect(this)">
+    <div class="letter">A</div>
+    <div class="content">
+      <h3>Single Column</h3>
+      <p>Clean, focused reading experience</p>
+    </div>
+  </div>
+  <div class="option" data-choice="b" onclick="toggleSelect(this)">
+    <div class="letter">B</div>
+    <div class="content">
+      <h3>Two Column</h3>
+      <p>Sidebar navigation with main content</p>
+    </div>
+  </div>
+</div>
+```
+
+That's it. No `<html>`, no CSS, no `<script>` tags needed. The server provides all of that.
+
+## CSS Classes Available
+
+The frame template provides these CSS classes for your content:
+
+### Options (A/B/C choices)
+
+```html
+<div class="options">
+  <div class="option" data-choice="a" onclick="toggleSelect(this)">
+    <div class="letter">A</div>
+    <div class="content">
+      <h3>Title</h3>
+      <p>Description</p>
+    </div>
+  </div>
+</div>
+```
+
+**Multi-select:** Add `data-multiselect` to the container to let users select multiple options. Each click toggles the item. The indicator bar shows the count.
+
+```html
+<div class="options" data-multiselect>
+  <!-- same option markup — users can select/deselect multiple -->
+</div>
+```
+
+### Cards (visual designs)
+
+```html
+<div class="cards">
+  <div class="card" data-choice="design1" onclick="toggleSelect(this)">
+    <div class="card-image"><!-- mockup content --></div>
+    <div class="card-body">
+      <h3>Name</h3>
+      <p>Description</p>
+    </div>
+  </div>
+</div>
+```
+
+### Mockup container
+
+```html
+<div class="mockup">
+  <div class="mockup-header">Preview: Dashboard Layout</div>
+  <div class="mockup-body"><!-- your mockup HTML --></div>
+</div>
+```
+
+### Split view (side-by-side)
+
+```html
+<div class="split">
+  <div class="mockup"><!-- left --></div>
+  <div class="mockup"><!-- right --></div>
+</div>
+```
+
+### Pros/Cons
+
+```html
+<div class="pros-cons">
+  <div class="pros"><h4>Pros</h4><ul><li>Benefit</li></ul></div>
+  <div class="cons"><h4>Cons</h4><ul><li>Drawback</li></ul></div>
+</div>
+```
+
+### Mock elements (wireframe building blocks)
+
+```html
+<div class="mock-nav">Logo | Home | About | Contact</div>
+<div style="display: flex;">
+  <div class="mock-sidebar">Navigation</div>
+  <div class="mock-content">Main content area</div>
+</div>
+<button class="mock-button">Action Button</button>
+<input class="mock-input" placeholder="Input field">
+<div class="placeholder">Placeholder area</div>
+```
+
+### Typography and sections
+
+- `h2` — page title
+- `h3` — section heading
+- `.subtitle` — secondary text below title
+- `.section` — content block with bottom margin
+- `.label` — small uppercase label text
+
+## Browser Events Format
+
+When the user clicks options in the browser, their interactions are recorded to `$STATE_DIR/events` (one JSON object per line). The file is cleared automatically when you push a new screen.
+
+```jsonl
+{"type":"click","choice":"a","text":"Option A - Simple Layout","timestamp":1706000101}
+{"type":"click","choice":"c","text":"Option C - Complex Grid","timestamp":1706000108}
+{"type":"click","choice":"b","text":"Option B - Hybrid","timestamp":1706000115}
+```
+
+The full event stream shows the user's exploration path — they may click multiple options before settling. The last `choice` event is typically the final selection, but the pattern of clicks can reveal hesitation or preferences worth asking about.
+
+If `$STATE_DIR/events` doesn't exist, the user didn't interact with the browser — use only their terminal text.
+
+## Design Tips
+
+- **Scale fidelity to the question** — wireframes for layout, polish for polish questions
+- **Explain the question on each page** — "Which layout feels more professional?" not just "Pick one"
+- **Iterate before advancing** — if feedback changes current screen, write a new version
+- **2-4 options max** per screen
+- **Use real content when it matters** — for a photography portfolio, use actual images (Unsplash). Placeholder content obscures design issues.
+- **Keep mockups simple** — focus on layout and structure, not pixel-perfect design
+
+## File Naming
+
+- Use semantic names: `platform.html`, `visual-style.html`, `layout.html`
+- Never reuse filenames — each screen must be a new file
+- For iterations: append version suffix like `layout-v2.html`, `layout-v3.html`
+- Server serves newest file by modification time
+
+## Cleaning Up
+
+```bash
+scripts/stop-server.sh $SESSION_DIR
+```
+
+If the session used `--project-dir`, mockup files persist in `.superpowers/brainstorm/` for later reference. Only `/tmp` sessions get deleted on stop.
+
+## Reference
+
+- Frame template (CSS reference): `scripts/frame-template.html`
+- Helper script (client-side): `scripts/helper.js`

+ 18 - 0
.claude/skills/claude-handoff/SKILL.md

@@ -0,0 +1,18 @@
+---
+name: claude-handoff
+description: Hand the current conversation off to a fresh background agent that picks up the work immediately.
+argument-hint: "What will the next session be used for?"
+disable-model-invocation: true
+---
+
+Write a handoff summary of the current conversation so a fresh agent can continue the work. Instead of saving it, launch a background agent seeded with the summary as its prompt: `claude --bg --name "<descriptive name>" "<handoff summary>"`. It starts in the current working directory and returns immediately; the user manages it with `claude agents`.
+
+Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`) — it sets the display name shown in the job list, session picker, and terminal title.
+
+Include a "suggested skills" section in the summary, naming which skills the next agent should call the Skill tool for.
+
+Do not duplicate content already captured in other artifacts (specs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
+
+Redact any sensitive information, such as API keys, passwords, or personally identifiable information — the summary becomes the agent's prompt.
+
+If the user passed arguments, treat them as a description of what the next session will focus on and tailor the summary accordingly.

+ 5 - 0
.claude/skills/claude-handoff/agents/openai.yaml

@@ -0,0 +1,5 @@
+interface:
+  display_name: "Claude Handoff"
+  short_description: "Hand off to a background agent"
+policy:
+  allow_implicit_invocation: false

+ 87 - 0
.claude/skills/code-review/SKILL.md

@@ -0,0 +1,87 @@
+---
+name: code-review
+description: Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/spec asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X".
+---
+
+Two-axis review of the diff between `HEAD` and a fixed point the user supplies:
+
+- **Standards** — does the code conform to this repo's documented coding standards?
+- **Spec** — does the code faithfully implement the originating issue / spec?
+
+Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings.
+
+The issue tracker should have been provided to you. If `docs/agents/issue-tracker.md` is missing, tell the user to run `/setup-matt-pocock-skills`.
+
+## Process
+
+### 1. Pin the fixed point
+
+Whatever the user said is the fixed point — a commit SHA, branch name, tag, `main`, `HEAD~5`, etc. If they didn't specify one, ask for it.
+
+Capture the diff command once: `git diff <fixed-point>...HEAD` (three-dot, so the comparison is against the merge-base). Also note the list of commits via `git log <fixed-point>..HEAD --oneline`.
+
+Before going further, confirm the fixed point resolves (`git rev-parse <fixed-point>`) and the diff is non-empty. A bad ref or empty diff should fail here — not inside two parallel sub-agents.
+
+### 2. Identify the spec source
+
+Look for the originating spec, in this order:
+
+1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.) — fetch via the workflow in `docs/agents/issue-tracker.md`.
+2. A path the user passed as an argument.
+3. A spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature.
+4. If nothing is found, ask the user where the spec is. If they say there isn't one, the **Spec** sub-agent will skip and report "no spec available".
+
+### 3. Identify the standards sources
+
+Anything in the repo that documents how code should be written, such as `CODING_STANDARDS.md` or `CONTRIBUTING.md`.
+
+On top of whatever the repo documents, the Standards axis always carries the **smell baseline** below — a fixed set of Fowler code smells (_Refactoring_, ch.3) that applies even when a repo documents nothing. Two rules bind it:
+
+- **The repo overrides.** A documented repo standard always wins; where it endorses something the baseline would flag, suppress the smell.
+- **Always a judgement call.** Each smell is a labelled heuristic ("possible Feature Envy"), never a hard violation — and, like any standard here, skip anything tooling already enforces.
+
+Each smell reads *what it is* → *how to fix*; match it against the diff:
+
+- **Mysterious Name** — a function, variable, or type whose name doesn't reveal what it does or holds. → rename it; if no honest name comes, the design's murky.
+- **Duplicated Code** — the same logic shape appears in more than one hunk or file in the change. → extract the shared shape, call it from both.
+- **Feature Envy** — a method that reaches into another object's data more than its own. → move the method onto the data it envies.
+- **Data Clumps** — the same few fields or params keep travelling together (a type wanting to be born). → bundle them into one type, pass that.
+- **Primitive Obsession** — a primitive or string standing in for a domain concept that deserves its own type. → give the concept its own small type.
+- **Repeated Switches** — the same `switch`/`if`-cascade on the same type recurs across the change. → replace with polymorphism, or one map both sites share.
+- **Shotgun Surgery** — one logical change forces scattered edits across many files in the diff. → gather what changes together into one module.
+- **Divergent Change** — one file or module is edited for several unrelated reasons. → split so each module changes for one reason.
+- **Speculative Generality** — abstraction, parameters, or hooks added for needs the spec doesn't have. → delete it; inline back until a real need shows.
+- **Message Chains** — long `a.b().c().d()` navigation the caller shouldn't depend on. → hide the walk behind one method on the first object.
+- **Middle Man** — a class or function that mostly just delegates onward. → cut it, call the real target direct.
+- **Refused Bequest** — a subclass or implementer that ignores or overrides most of what it inherits. → drop the inheritance, use composition.
+
+### 4. Spawn both sub-agents in parallel
+
+**Standards sub-agent prompt** — include:
+
+- The full diff command and commit list.
+- The list of standards-source files you found in step 3, **plus the smell baseline from step 3** pasted in full — the sub-agent has no other access to it.
+- The brief: "Report — per file/hunk where relevant — (a) every place the diff violates a documented standard: cite the standard (file + the rule); and (b) any baseline smell you spot: name it and quote the hunk. Distinguish hard violations from judgement calls — documented-standard breaches can be hard, but baseline smells are always judgement calls, and a documented repo standard overrides the baseline. Skip anything tooling enforces. Under 400 words."
+
+**Spec sub-agent prompt** — include:
+
+- The diff command and commit list.
+- The path or fetched contents of the spec.
+- The brief: "Report: (a) requirements the spec asked for that are missing or partial; (b) behaviour in the diff that wasn't asked for (scope creep); (c) requirements that look implemented but where the implementation looks wrong. Quote the spec line for each finding. Under 400 words."
+
+If the spec is missing, skip the Spec sub-agent and note this in the final report.
+
+### 5. Aggregate
+
+Present the two reports under `## Standards` and `## Spec` headings, verbatim or lightly cleaned. Do **not** merge or rerank findings — the two axes are deliberately separate (see _Why two axes_).
+
+End with a one-line summary: total findings per axis, and the worst issue _within each axis_ (if any). Don't pick a single winner across axes — that's the reranking the separation exists to prevent.
+
+## Why two axes
+
+A change can pass one axis and fail the other:
+
+- Code that follows every standard but implements the wrong thing → **Standards pass, Spec fail.**
+- Code that does exactly what the issue asked but breaks the project's conventions → **Spec pass, Standards fail.**
+
+Reporting them separately stops one axis from masking the other.

+ 3 - 0
.claude/skills/code-review/agents/openai.yaml

@@ -0,0 +1,3 @@
+interface:
+  display_name: "Code Review"
+  short_description: "Review a diff on standards and spec"

+ 120 - 0
.claude/skills/code/SKILL.md

@@ -0,0 +1,120 @@
+---
+name: Code
+slug: code
+version: 1.0.4
+homepage: https://clawic.com/skills/code
+description: Coding workflow with planning, implementation, verification, and testing for clean software development.
+changelog: Improved description for better discoverability
+metadata: {"clawdbot":{"emoji":"💻","requires":{"bins":[]},"os":["linux","darwin","win32"]}}
+---
+
+## When to Use
+
+User explicitly requests code implementation. Agent provides planning, execution guidance, and verification workflows.
+
+## Architecture
+
+User preferences stored in `~/code/` when user explicitly requests.
+
+```
+~/code/
+  - memory.md    # User-provided preferences only
+```
+
+Create on first use: `mkdir -p ~/code`
+
+## Quick Reference
+
+| Topic | File |
+|-------|------|
+| Memory setup | `memory-template.md` |
+| Task breakdown | `planning.md` |
+| Execution flow | `execution.md` |
+| Verification | `verification.md` |
+| Multi-task state | `state.md` |
+| User criteria | `criteria.md` |
+
+## Scope
+
+This skill ONLY:
+- Provides coding workflow guidance
+- Stores preferences user explicitly provides in `~/code/`
+- Reads included reference files
+
+This skill NEVER:
+- Executes code automatically
+- Makes network requests
+- Accesses files outside `~/code/` and the user's project
+- Modifies its own SKILL.md or auxiliary files
+- Takes autonomous action without user awareness
+
+## Core Rules
+
+### 1. Check Memory First
+Read `~/code/memory.md` for user's stated preferences if it exists.
+
+### 2. User Controls Execution
+- This skill provides GUIDANCE, not autonomous execution
+- User decides when to proceed to next step
+- Sub-agent delegation requires user's explicit request
+
+### 3. Plan Before Code
+- Break requests into testable steps
+- Each step independently verifiable
+- See `planning.md` for patterns
+
+### 4. Verify Everything
+| After | Do |
+|-------|-----|
+| Each function | Suggest running tests |
+| UI changes | Suggest taking screenshot |
+| Before delivery | Suggest full test suite |
+
+### 5. Store Preferences on Request
+| User says | Action |
+|-----------|--------|
+| "Remember I prefer X" | Add to memory.md |
+| "Never do Y again" | Add to memory.md Never section |
+
+Only store what user explicitly asks to save.
+
+## Workflow
+
+```
+Request -> Plan -> Execute -> Verify -> Deliver
+```
+
+## Common Traps
+
+- **Delivering untested code** -> always verify first
+- **Huge PRs** -> break into testable chunks
+- **Ignoring preferences** -> check memory.md first
+
+## Self-Modification
+
+This skill NEVER modifies its own SKILL.md or auxiliary files.
+User data stored only in `~/code/memory.md` after explicit request.
+
+## External Endpoints
+
+This skill makes NO network requests.
+
+| Endpoint | Data Sent | Purpose |
+|----------|-----------|---------|
+| None | None | N/A |
+
+## Security & Privacy
+
+**Data that stays local:**
+- Only preferences user explicitly asks to save
+- Stored in `~/code/memory.md`
+
+**Data that leaves your machine:**
+- None. This skill makes no network requests.
+
+**This skill does NOT:**
+- Execute code automatically
+- Access network or external services  
+- Access files outside `~/code/` and user's project
+- Take autonomous actions without user awareness
+- Delegate to sub-agents without user's explicit request

+ 6 - 0
.claude/skills/code/_meta.json

@@ -0,0 +1,6 @@
+{
+  "ownerId": "kn73vp5rarc3b14rc7wjcw8f8580t5d1",
+  "slug": "code",
+  "version": "1.0.4",
+  "publishedAt": 1771467169291
+}

+ 48 - 0
.claude/skills/code/criteria.md

@@ -0,0 +1,48 @@
+# Criteria for Storing Preferences
+
+Reference for when to save user preferences to `~/code/memory.md`.
+
+## When to Save (User Must Request)
+
+Save only when user explicitly asks:
+- "Remember that I prefer X"
+- "Always do Y from now on"
+- "Save this preference"
+- "Don't forget that I like Z"
+
+## When NOT to Save
+
+- User didn't explicitly ask to save
+- Project-specific requirement (applies to this project only)
+- One-off request ("just this once")
+- Temporary preference
+
+## What to Save
+
+**Preferences:**
+- Coding style preferences user stated
+- Tools or frameworks user prefers
+- Patterns user explicitly likes
+
+**Things to avoid:**
+- Approaches user explicitly dislikes
+- Patterns user asked not to repeat
+
+## Format in memory.md
+
+```markdown
+## Preferences
+- prefers TypeScript over JavaScript
+- likes detailed comments
+- wants tests for all functions
+
+## Never
+- no class-based React components
+- avoid inline styles
+```
+
+## Important
+
+- Only save what user EXPLICITLY asked to save
+- Ask user before saving: "Should I remember this preference?"
+- Never modify any skill files, only `~/code/memory.md`

+ 42 - 0
.claude/skills/code/execution.md

@@ -0,0 +1,42 @@
+# Execution Guidance
+
+Reference for executing multi-step implementations.
+
+## Recommended Flow
+
+When user approves a step:
+1. Execute that step
+2. Verify it works
+3. Report completion to user
+4. Wait for user to approve next step
+
+## Progress Tracking
+
+Show user the current state:
+```
+- [DONE] Step 1 (completed)
+- [WIP] Step 2 <- awaiting user approval
+- [ ] Step 3
+- [ ] Step 4
+```
+
+## When to Pause and Ask User
+
+- Before starting any new step
+- When encountering an error
+- When a decision is needed (A vs B)
+- When credentials or permissions are needed
+
+## Error Handling
+
+If an error occurs:
+1. Report the error to user
+2. Suggest possible fixes
+3. Wait for user decision on how to proceed
+
+## Patterns to Follow
+
+- Report completion of each step
+- Ask before proceeding to next step
+- Let user decide retry strategy
+- Keep user informed of progress

+ 38 - 0
.claude/skills/code/memory-template.md

@@ -0,0 +1,38 @@
+# Memory Setup - Code
+
+## Initial Setup
+
+Create directory on first use:
+```bash
+mkdir -p ~/code
+touch ~/code/memory.md
+```
+
+## memory.md Template
+
+Copy to `~/code/memory.md`:
+
+```markdown
+# Code Memory
+
+## Preferences
+<!-- User's coding workflow preferences. Format: "preference" -->
+<!-- Examples: always run tests, prefer TypeScript, commit after each feature -->
+
+## Never
+<!-- Things that don't work for this user. Format: "thing to avoid" -->
+<!-- Examples: inline styles, console.log debugging, large PRs -->
+
+## Patterns
+<!-- Approaches that work well. Format: "pattern: context" -->
+<!-- Examples: TDD: for complex logic, screenshots: for UI work -->
+
+---
+Last updated: YYYY-MM-DD
+```
+
+## Notes
+
+- Check `criteria.md` for additional user-specific criteria
+- Use `planning.md` for breaking down complex requests
+- Verify with tests and screenshots per `verification.md`

+ 31 - 0
.claude/skills/code/planning.md

@@ -0,0 +1,31 @@
+# Planning Reference
+
+Consult when breaking down a multi-step request.
+
+## When to Plan
+- Multiple files or components
+- Dependencies between parts
+- UI that needs visual verification
+- User says "build", "create", "implement"
+
+## Step Format
+```
+Step N: [What]
+- Output: [What exists after]
+- Test: [How to verify]
+```
+
+## Good Steps
+- Clear output (file, endpoint, screen)
+- Testable independently
+- No ambiguity in what "done" means
+
+## Bad Steps
+- "Implement the thing" (vague output)
+- No test defined
+- Depends on undefined prior step
+
+## Don't Plan
+- One-liner functions
+- Simple modifications
+- Questions about existing code

+ 60 - 0
.claude/skills/code/state.md

@@ -0,0 +1,60 @@
+# State Tracking Guidance
+
+Reference for tracking multiple tasks or requests.
+
+## Request Tracking
+
+Label each user request:
+```
+[R1] Build login page
+[R2] Add dark mode
+[R3] Fix header alignment
+```
+
+Track state for user visibility:
+```
+[R1] [DONE] Done
+[R2] [WIP] In progress (awaiting user approval for step 2)
+[R3] [Q] Queued
+```
+
+## Managing Multiple Requests
+
+When user sends a new request while another is in progress:
+
+1. Acknowledge: "Got it, I'll add this to the queue"
+2. Show updated queue to user
+3. Ask user if priority should change
+
+## Handling Interruptions
+
+| Situation | Suggested Action |
+|-----------|------------------|
+| New unrelated request | Add to queue, ask user priority |
+| Request affects current work | Pause, explain impact, ask user how to proceed |
+| User says "stop" or "wait" | Stop immediately, await instructions |
+| User changes requirements | Summarize impact, ask user to confirm changes |
+
+## User Decisions
+
+Always ask user before:
+- Starting work on queued items
+- Changing priority order
+- Rolling back completed work
+- Modifying the plan
+
+## Progress File (Optional)
+
+User may request a state file:
+```markdown
+## In Progress
+[R2] Dark mode - Step 2/4 (awaiting user approval)
+
+## Queued  
+[R3] Header fix
+
+## Done
+[R1] Login page [DONE]
+```
+
+Update only when user requests or approves changes.

+ 39 - 0
.claude/skills/code/verification.md

@@ -0,0 +1,39 @@
+# Verification Reference
+
+Consult when verifying implementations visually or with tests.
+
+## Screenshots
+- Wait for full page load (no spinners)
+- Review yourself before sending
+- Split long pages into 3-5 sections (~800px each)
+- Caption each: "Hero", "Features", "Footer"
+
+## Before Sending
+```
+[ ] Content loaded
+[ ] Shows the specific change
+[ ] No visual bugs
+[ ] Caption explains what user sees
+```
+
+## Fix-Before-Send
+If screenshot shows problem:
+1. Fix code
+2. Re-deploy
+3. New screenshot
+4. Still broken? -> back to 1
+5. Fixed? -> now send
+
+Never send "I noticed X is wrong, will fix" - fix first.
+
+## No UI? Show Output
+
+When verifying API endpoints, show actual output:
+```
+GET /api/users -> {"id": 1, "name": "test"}
+```
+
+Include actual response, not just "it works".
+
+## Flows
+Number sequential states: "1/4: Form", "2/4: Loading", "3/4: Error", "4/4: Success"

+ 37 - 0
.claude/skills/codebase-design/DEEPENING.md

@@ -0,0 +1,37 @@
+# Deepening
+
+How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](SKILL.md) — **module**, **interface**, **seam**, **adapter**.
+
+## Dependency categories
+
+When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
+
+### 1. In-process
+
+Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.
+
+### 2. Local-substitutable
+
+Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.
+
+### 3. Remote but owned (Ports & Adapters)
+
+Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
+
+Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."*
+
+### 4. True external (Mock)
+
+Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.
+
+## Seam discipline
+
+- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
+- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
+
+## Testing strategy: replace, don't layer
+
+- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
+- Write new tests at the deepened module's interface. The **interface is the test surface**.
+- Tests assert on observable outcomes through the interface, not internal state.
+- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.

+ 44 - 0
.claude/skills/codebase-design/DESIGN-IT-TWICE.md

@@ -0,0 +1,44 @@
+# Design It Twice
+
+When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
+
+Uses the vocabulary in [SKILL.md](SKILL.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
+
+## Process
+
+### 1. Frame the problem space
+
+Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
+
+- The constraints any new interface would need to satisfy
+- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md))
+- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
+
+Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
+
+### 2. Spawn sub-agents
+
+Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the deepened module.
+
+Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
+
+- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point."
+- Agent 2: "Maximise flexibility — support many use cases and extension."
+- Agent 3: "Optimise for the most common caller — make the default case trivial."
+- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
+
+Include both [SKILL.md](SKILL.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
+
+Each sub-agent outputs:
+
+1. Interface (types, methods, params — plus invariants, ordering, error modes)
+2. Usage example showing how callers use it
+3. What the implementation hides behind the seam
+4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md))
+5. Trade-offs — where leverage is high, where it's thin
+
+### 3. Present and compare
+
+Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**.
+
+After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu.

+ 114 - 0
.claude/skills/codebase-design/SKILL.md

@@ -0,0 +1,114 @@
+---
+name: codebase-design
+description: Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
+---
+
+# Codebase Design
+
+Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.
+
+## Glossary
+
+Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.
+
+**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service.
+
+**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface).
+
+**Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
+
+**Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation.
+
+**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context).
+
+**Adapter** — a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside).
+
+**Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests.
+
+**Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere.
+
+## Deep vs shallow
+
+**Deep module** = small interface + lots of implementation:
+
+```
+┌─────────────────────┐
+│   Small Interface   │  ← Few methods, simple params
+├─────────────────────┤
+│                     │
+│  Deep Implementation│  ← Complex logic hidden
+│                     │
+└─────────────────────┘
+```
+
+**Shallow module** = large interface + little implementation (avoid):
+
+```
+┌─────────────────────────────────┐
+│       Large Interface           │  ← Many methods, complex params
+├─────────────────────────────────┤
+│  Thin Implementation            │  ← Just passes through
+└─────────────────────────────────┘
+```
+
+When designing an interface, ask:
+
+- Can I reduce the number of methods?
+- Can I simplify the parameters?
+- Can I hide more complexity inside?
+
+## Principles
+
+- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
+- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
+- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape.
+- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.
+
+## Designing for testability
+
+Good interfaces make testing natural:
+
+1. **Accept dependencies, don't create them.**
+
+   ```typescript
+   // Testable
+   function processOrder(order, paymentGateway) {}
+
+   // Hard to test
+   function processOrder(order) {
+     const gateway = new StripeGateway();
+   }
+   ```
+
+2. **Return results, don't produce side effects.**
+
+   ```typescript
+   // Testable
+   function calculateDiscount(cart): Discount {}
+
+   // Hard to test
+   function applyDiscount(cart): void {
+     cart.total -= discount;
+   }
+   ```
+
+3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup.
+
+## Relationships
+
+- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
+- **Depth** is a property of a **Module**, measured against its **Interface**.
+- A **Seam** is where a **Module**'s **Interface** lives.
+- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
+- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
+
+## Rejected framings
+
+- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
+- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.
+- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**.
+
+## Going deeper
+
+- **Deepening a cluster given its dependencies** — see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing.
+- **Exploring alternative interfaces** — see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): spin up parallel sub-agents to design the interface several radically different ways, then compare on depth, locality, and seam placement.

+ 3 - 0
.claude/skills/codebase-design/agents/openai.yaml

@@ -0,0 +1,3 @@
+interface:
+  display_name: "Codebase Design"
+  short_description: "Vocabulary for deep-module design"

+ 63 - 0
.claude/skills/coding-guide/SKILL.md

@@ -0,0 +1,63 @@
+# 编码行为准则
+
+以下准则优先于默认行为,请在所有编码工作中遵守。
+
+## 1. 先思考,再编码
+
+**不要假设。不要隐藏疑问。明确列出取舍。**
+
+实现之前:
+- 明确陈述你的假设。如果不确定,直接问。
+- 如果有多种理解方式,全部列出——不要默默选一个。
+- 如果有更简单的方案,说出来。必要时 push back。
+- 如果某事不清楚,停下来。指出困惑点。开口问。
+
+## 2. 简洁优先
+
+**用最少的代码解决问题。不写投机性代码。**
+
+- 不添加需求之外的功能。
+- 不为单次使用的代码创建抽象层。
+- 不添加未被请求的"灵活性"或"可配置性"。
+- 不为不可能发生的场景添加错误处理。
+- 如果你写了 200 行,但 50 行就够,重写。
+
+自问:"一个高级工程师看了会说这过度设计吗?" 如果答案是肯定的,简化。
+
+## 3. 手术式修改
+
+**只动必须动的。只清理你自己造成的混乱。**
+
+编辑现有代码时:
+- 不要"顺便优化"邻近的代码、注释或格式。
+- 不要重构没坏的东西。
+- 匹配现有风格,即使与你偏好的不同。
+- 如果发现无关的死代码,提出来——不要删。
+
+当你的修改造成孤立代码时:
+- 删除**你的修改**导致不再使用的 import/变量/函数。
+- 不要删除已有的死代码,除非被要求。
+
+检验标准:每一行改动都必须能追溯到用户的具体请求。
+
+## 4. 目标驱动执行
+
+**定义成功标准。循环直到验证通过。**
+
+把任务转化为可验证的目标:
+- "添加验证" → "先写无效输入的测试,再让测试通过"
+- "修复 bug" → "先写复现测试,再修复"
+- "重构 X" → "确保重构前后测试都通过"
+
+多步骤任务,先列出简要计划:
+```
+1. [步骤] → 验证方式: [检查项]
+2. [步骤] → 验证方式: [检查项]
+3. [步骤] → 验证方式: [检查项]
+```
+
+强大的成功标准让你能独立迭代。模糊的标准("把它做出来")需要不断追问澄清。
+
+---
+
+**这些准则有效的标志:** diff 中少了不必要的改动,少了因过度设计导致的重写,澄清性问题在实现之前出现而非在犯错之后。

+ 671 - 0
.claude/skills/dev-expert/FAQ.md

@@ -0,0 +1,671 @@
+# 常见问题答疑(FAQ)
+
+本文件集中索引 dev-expert 技能使用中常见疑问、执行禁区、验证失败处置和边界外请求处理。详细规则出处以章节名标注,便于追溯到 SKILL.md 或对应 reference。
+
+> 📌 **TL;DR(30 秒速览)**
+>
+> - 不会选技能?**直接描述目标**,系统自动路由+组合,选错会提示确认,不会乱干。
+> - 怕踩坑?先扫「能力边界速览」看哪些不能做;红线(直拼 SQL/盲写库/跳验证)必被拦。
+> - 长任务/换电脑?靠 `.ai-memory/handoff.md` 检查点续做,进度不丢。
+> - 改错了?`.bak` 自动备份,复制回原文件即回滚(**别手动删 `.bak`**)。
+> - 看不懂报错?要求"普通人可读版",必须给"发生了什么/原因/方向"。
+
+### FAQ 主题速查
+
+> 🚀 **第一次用?只看这段**:直接说你的需求,系统自动路由+组合;只有"报错 / 不确定能不能做 / 想纠正"才需要往下翻。
+
+<details>
+<summary>需要按主题查找?点开速查表</summary>
+
+| 你想了解                                                   | 看哪节                                                   |
+| ---------------------------------------------------------- | -------------------------------------------------------- |
+| 怎么选 / 触发哪个技能、关键词没命中怎么办                  | 一、路由与子技能选择                                     |
+| 哪些事不能做(执行禁区 / 边界外)                          | 二、执行禁区清单 / 六、边界外请求 / 顶部「能力边界速览」 |
+| 报错看不懂、验证失败怎么处置                               | 三、验证失败处置 / 四、异常处理与稳定性                  |
+| 常见反模式与踩坑                                           | 五、常见反模式与踩坑                                     |
+| 显式 `@` 调用与自动路由                                    | 七、显式调用与自动路由                                   |
+| 子 Agent 能不能改代码                                      | 八、子Agent 边界                                         |
+| 长任务续做 / 检查点 / 可靠交付 / 权限开放                  | 九、长任务执行与可靠产出物                               |
+| 数据库 / 迁移 / 慢查询安全                                 | 十、数据库与迁移安全                                     |
+| CMS / Laravel / Java 专项边界                              | 十一、专项领域与框架边界                                 |
+| 项目记忆 / 换电脑续做                                      | 十二、新会话恢复与项目记忆                               |
+| 文档 / Spec / 技术选型                                     | 十三、文档与规范类任务                                   |
+| JS / Node.js 专项                                          | 十四、JavaScript 专项                                    |
+| Hooks 自动守卫(启用 / 检查 / 排查 / 禁用)                | 十五、Hooks 自动守卫                                     |
+| 需求澄清 / Wayfinder / 模块边界                            | 十六、需求澄清与模块边界                                 |
+| 交付保障 / 收尾报告 / 踩坑错误册                           | 十七、交付保障与踩坑管理                                 |
+| 多技能组合与触发进阶                                       | 十八、子技能组合与触发进阶                               |
+| 新手常问但前面没答的问题(纠正/回滚/中文/省 token/第一步) | 二十、新手高频疑问补遗                                   |
+| 可直接抄用的代码示例                                       | 十九、常见技能实际代码示例                               |
+
+</details>
+
+## 先看这里:普通人可读版
+
+> **本节要点**:新人先读这节——"你遇到的情况"对照表 + 能力边界速览,一眼分清能做/不能做。
+
+| 你遇到的情况               | 先做什么                                     | 不要做什么                           |
+| -------------------------- | -------------------------------------------- | ------------------------------------ |
+| 不知道该用哪个技能         | 直接描述目标,例如"帮我修这个报错"           | 不必先背全部触发词                   |
+| 看不懂错误提示             | 要求输出"发生了什么 / 可能原因 / 修复方向"   | 不要只根据专业术语改配置             |
+| 网络卡住或下载失败         | 允许最多 2 次安全重试,并记录最后错误        | 不要对写入、扣费、发布类操作自动重试 |
+| 涉及数据库、权限、生产配置 | 先看方案、影响范围、回滚和验证路径           | 不要直接进入快速通道                 |
+| 文档太多不知道看哪个       | README 看入口,FAQ 看踩坑,references 看细节 | 不要同时读全量 reference             |
+
+### 能力边界速览:哪些事本技能明确不做(一眼看清,免得踩坑)
+
+很多人翻到 FAQ 深处才发现"原来这不能做"。这里先集中摆一眼,**完整清单仍在 二、执行禁区清单 与 六、边界外请求**。
+
+**① 明确不覆盖的领域(边界外,只做说明不替代专门流程)**
+用户调研 · 工时排期 · 容器深度编排 · IDE 配置 · 缺陷跟踪流程 · 安全深度扫描。
+
+**② 明确禁止的做法(执行禁区,踩了会被安全闸门拦 / 交付无效)**
+直拼 SQL · Shell 写中文/UTF-8 文件 · 跨模块扩散修改 · 硬编码密钥 · 跳过验证直接交付 · 安全/数据类失败重试 · 凭猜测补业务规则 · 快速通道执行数据库写入 · 跨模块(≥2)/批量替换(>10处) 走快速通道。
+
+**③ 用户最容易忽略的技术限制(放在显眼处提醒)**
+
+- **单次最多加载 3 个 reference**:一句话挂太多子技能会触发"过度路由"回退,只保留首选 + 必要协同(见 一、Q4)。
+- **子 Agent 禁止写/改代码(L0 硬约束)**:只做检索收集,改码须主 Agent 自写或复核后执行(见 八、)。
+- **代码审查是只读的**:`@code-review` 只出分级问题清单,**不默认改写实现**;要改须走重构/生成分支并先确认(见 十八、)。
+- **十一个"专项 reference"不支持 `@` 显式调用**:`@laravel-development` / `@laravel-testing` / `@java-development` / `@javascript-development` / `@execution-safety` / `@delivery-assurance` / `@error-ledger` / `@style-alignment` / `@architecture-decision` / `@domain-driven-design` / `@distributed-systems` 只作领域协同资料,用关键词触发即可(见 七、Q5 / 十四、Q1)。
+- **快速通道有门槛**:仅低风险、可局部回退、目标明确时启用;涉及库写入 / 权限 / 生产配置须退出,先给方案与回滚路径(见 四、Q"哪些操作前必须确认")。
+- **hooks 是护栏,不替代 Agent 验证**:只兜底机械项(备份/lint/PHP8/脱敏…),真实运行与 E2E 仍须 Agent 显式产出证据(见 十五、Q"hooks 能替代吗")。
+
+> 记不住全表没关系,记住一句:**"写代码/查 Bug/做文档/审质量"都能做;问调研排期、动生产配置前先确认、别让 Agent 替你盲写库"就是边界。**
+
+## 一、路由与子技能选择
+
+> **本节要点**:没命中关键词/显式@/多领域/跨模块怎么路由,单次最多加载 3 个 reference。
+
+**Q1:我说的话没命中任何关键词怎么办?**
+A:优先用 `@<英文标识>` 显式指定(见 SKILL.md Step 1.2),或在输入中描述任务目标而非关键词(如"加个登录接口"会被路由到 `api-design`)。仍无法命中时,系统会按"无法唯一确定时向用户确认意图"规则向你索取意图。
+
+**Q2:显式指定后,发现任务实际不该用这个子技能,会自动切换吗?**
+A:不会自动切换。系统检测到强冲突时输出提示让你确认(见 SKILL.md Step 1.2 「任务-技能不匹配提示」表),你回复后才会切换;回复"按原指定执行"时立即按显式指定走,不再追问。
+
+**Q3:多个子技能同时命中怎么决定?**
+A:按 SKILL.md「子技能优先级矩阵」组合路由;无法组合时选匹配度最高的;无法唯一确定时向你确认。
+
+**Q4:任务跨多个领域(如 CMS + 数据库 + 前端)怎么处理?**
+A:按 SKILL.md「领域路由表」的协同 reference 顺序加载,先执行首选 reference,再按需加载协同 reference。一次任务最多加载 3 个 reference,超过则触发"过度路由"回退(见 SKILL.md「对话流异常处理边界」)。
+
+## 二、执行禁区清单
+
+> **本节要点**:9 条明确禁止做法 + 每禁区真实场景对照;踩了交付无效或被安全闸门拦。
+
+以下做法在本技能中明确禁止,踩坑会导致交付无效或被安全闸门拦截:
+
+| 禁区                                      | 原因                   | 正确做法                                    | 规则出处                              |
+| ----------------------------------------- | ---------------------- | ------------------------------------------- | ------------------------------------- |
+| 直拼 SQL 字符串                           | SQL 注入风险           | 用 PDO 预处理 / 参数绑定                    | `mysql-database`                      |
+| Shell 写中文/UTF-8 文件                   | 编码损坏、中文乱码     | 用 Edit / Write 工具,或二进制读写          | SKILL.md 非 Git 文件操作安全协议      |
+| 跨模块扩散修改                            | 破坏上下文延迟加载协议 | 一次只改当前目标文件 + 直接引用文件         | SKILL.md 上下文延迟加载协议           |
+| 硬编码密钥/Token/密码                     | 安全闸门拦截           | 从环境变量读取,密钥不入代码                | SKILL.md 安全闸门 / `cms-development` |
+| 跳过验证直接交付                          | 违反 Step 4 硬性前提   | 必须采集命令输出/测试报告/截图/API 响应之一 | SKILL.md Step 4 验证结果              |
+| 安全/数据类失败重试                       | 失败重试基线 = 0       | 立即升级,不重试                            | SKILL.md 失败重试基线                 |
+| 凭猜测补齐业务规则                        | 业务规则不明时禁止     | 高澄清级别,向用户索取输入                  | SKILL.md 澄清策略分级                 |
+| 快速通道执行数据库写入                    | 退出快速通道条件       | 切换到"先方案后执行"模式                    | SKILL.md 快速通道机制                 |
+| 跨模块(≥2)/批量替换(>10处) 在快速通道执行 | 误判快速通道           | 立即退出快速通道                            | SKILL.md 对话流异常处理边界           |
+
+### 二(续)· 每个禁区的具体场景举例
+
+上面表格的「原因 / 正确做法」偏原则,下面给每个禁区配一个**真实可对照**的场景,方便新人"对号入座":
+
+- **直拼 SQL 字符串**
+  ❌ `mysql_query("SELECT * FROM user WHERE id=".$_GET['id'])` —— 传入 `1; DROP TABLE user` 即注入脱裤。
+  ✅ PDO `WHERE id = ?` 并 `bindParam`,或走框架访问层传参(`mysql-database`)。
+- **Shell 写中文/UTF-8 文件**
+  ❌ PowerShell `Set-Content -Value "中文配置" x.txt` 默认 GBK/带 BOM,跨平台读成乱码。
+  ✅ 用 Edit/Write 工具写盘,或 Python `open(f, encoding='utf-8')` 二进制写。
+- **跨模块扩散修改**
+  ❌ 改一个工具函数,顺手把 8 个"看着像"的无关模块调用一起改 —— 破坏延迟加载,引发未知编译错误。
+  ✅ 只改当前目标文件 + 它直接 import 的文件;其余列入 `*_plan.md` 由你确认。
+- **硬编码密钥/Token/密码**
+  ❌ `api_key = "sk-live-xxxx"` 写进源码并提交。
+  ✅ 从环境变量 / 密钥管理读取,代码只留 `os.getenv("API_KEY")`。
+- **跳过验证直接交付**
+  ❌ "改完了,应该没问题" —— 无 lint / 测试 / 运行输出。
+  ✅ 至少贴一种证据:命令输出、测试报告、截图或 API 响应(Step 4 硬性前提)。
+- **安全/数据类失败重试**
+  ❌ 扣费接口 500 后自动再发一次 —— 可能重复扣费。
+  ✅ 立即升级报你,绝不自动重试(失败重试基线 = 0)。
+- **凭猜测补齐业务规则**
+  ❌ 看到字段空,自己猜"默认走 A 渠道"就写死。
+  ✅ 高澄清级别,先问你"默认渠道是 A 吗?"再动手。
+- **快速通道执行数据库写入**
+  ❌ "顺手把这条脏数据 UPDATE 掉" —— 没给影响行数和回滚。
+  ✅ 切"先方案后执行"模式:SELECT 验证 → 给影响行数 → 备回滚 SQL → 你确认。
+- **跨模块/批量替换走快速通道**
+  ❌ 在快速通道里一次性替换 15 处字符串。
+  ✅ 立即退出快速通道,输出方案 + 验证路径,按「批量修改 7 防线」做。
+
+## 三、验证失败处置
+
+> **本节要点**:按失败类型查重试上限(安全类 0 次、Lint 3 次、验证 2 次、环境 1 次)。
+
+按失败类型索引到现有规则,避免重复定义:
+
+| 失败类型               | 最大重试 | 处置                                 | 规则出处                                   |
+| ---------------------- | -------- | ------------------------------------ | ------------------------------------------ |
+| 安全/数据类            | 0        | 立即升级,不重试                     | SKILL.md 失败重试基线                      |
+| Lint/语法类            | 3        | 多次尝试修复                         | SKILL.md 失败重试基线                      |
+| 验证类失败             | 2        | 默认 2 次后升级                      | SKILL.md 失败重试基线                      |
+| 环境类失败             | 1        | 重试 1 次后升级                      | SKILL.md 失败重试基线                      |
+| 业务规则不明           | 0        | 立即向用户索取输入                   | SKILL.md 失败重试基线                      |
+| 同一 Bug 2 轮未修复    | —        | 标记已知限制,不再循环               | SKILL.md 轮次控制 → 失败熔断               |
+| 连续 2 轮仅做 P2 微调  | —        | 建议提前结束                         | SKILL.md 轮次控制 → 低收益检测             |
+| 加载 reference 数 > 3  | —        | 回到意图三分法,只保留首选 reference | SKILL.md 对话流异常处理边界 → 过度路由     |
+| 快速通道发现跨模块(≥2) | —        | 立即退出快速通道,输出方案和验证路径 | SKILL.md 对话流异常处理边界 → 快速通道误判 |
+| 输出含 Token/密码/密钥 | —        | 回到安全闸门,隔离不可信输入         | SKILL.md 对话流异常处理边界 → 安全闸门失效 |
+
+## 四、异常处理与稳定性
+
+> **本节要点**:报错须给"普通人可读版";生产/库/权限操作前必确认;网络仅安全场景重试 2 次。
+
+**Q:出了 Bug 时,会怎么告诉我?**
+A:必须给出"发生了什么、可能原因、修复方向、验证方法"。如果根因证据不足,只能说"最可能原因"和"待验证动作",不能把猜测写成结论。
+
+**Q:专业报错看不懂怎么办?**
+A:要求输出普通人可读版。标准格式是:
+
+```text
+发生了什么:[一句话解释]
+可能原因:[1-3 个候选原因,标注已确认/待验证]
+修复方向:[下一步最小动作]
+需要你提供:[缺什么就写什么,没有则写"无"]
+```
+
+**Q:哪些操作前必须确认?**
+A:生产配置、数据库写入、权限、凭证、隐私、不可回滚操作、发布上线、批量替换、删除文件、扣费或发消息类操作,都必须先说明影响范围和验证/回滚路径,等用户确认后再执行。
+
+**Q:网络卡顿会自动重试吗?**
+A:只在安全场景自动重试。下载依赖、只读请求、临时超时、远程 5xx 可以最多重试 2 次;写操作、扣费、发邮件、发布、删除、权限失败、参数错误、4xx 业务错误不自动重试。
+
+**Q:连续失败会不会偷偷继续?**
+A:不会。失败会按 SKILL.md "失败重试基线"熔断:安全/数据类不重试,验证类默认 2 次,Lint/语法类最多 3 次,环境类 1 次。超过后必须说明阻塞和下一步。
+
+**Q:什么情况下会停下来问我,而不是直接继续?**
+A:三类必问:① 涉及生产配置、数据库写入、权限、凭证、隐私或不可回滚操作 → 触发安全闸门,退出快速通道先给方案与验证路径;② 业务规则/验收口径不明 → 按澄清策略分级,高澄清级别先问关键问题再执行;③ 自主度随验证失败、范围扩大或新风险动态下调一级,关键步骤前确认。低风险、可局部回退、目标明确的任务则进快速通道直接做,并在交付里说明假设与证据(见 SKILL.md「自主度分级」「澄清策略分级」)。
+
+## 五、常见反模式与踩坑
+
+> **本节要点**:16 条常见反模式对照表,提前避坑(含"未验证就说完成""审查却直接改实现"等)。
+
+| 反模式 / 坑            | 典型表现                                   | 正确做法                                                                 |
+| ---------------------- | ------------------------------------------ | ------------------------------------------------------------------------ |
+| 只看 README 不看 FAQ   | 初次能启动,但遇到失败不知道怎么办         | README 看入口,FAQ 查踩坑和边界                                          |
+| 一次加载太多 reference | 文档越看越乱,任务边界变大                 | 单任务最多首选 + 必要协同 reference                                      |
+| 未验证就说完成         | 没有命令、测试、截图或 API 响应证据        | 按 Step 4 输出至少一种验证证据                                           |
+| 把优化当主线           | 主功能没验收,却开始整理命名/注释          | 主线完成度只按用户核心交付物计算                                         |
+| 网络失败反复重试       | 下载/接口失败后无限循环                    | 最多 2 次安全重试,之后输出阻塞                                          |
+| 数据库写入走快速通道   | 未确认影响行数和回滚路径                   | 先方案后执行,必要时 SELECT/EXPLAIN 验证                                 |
+| 报错只给专业术语       | 用户只看到堆栈和错误码                     | 同时给普通人可读解释和下一步动作                                         |
+| 换电脑只依赖 IDE 会话  | 新电脑不知道上次做到哪                     | 写 `.ai-memory/handoff.md` 并随代码同步                                  |
+| 修改缓存文件修报错     | 下次缓存重建后问题复发                     | 排查上游生成逻辑,不直接改缓存                                           |
+| Shell 改中文文件       | UTF-8/GBK 被破坏导致乱码                   | 使用安全编辑方式,避免 `Set-Content` / `Out-File`                        |
+| 长任务不落检查点       | 对话压缩/关 IDE 后进度全丢,重跑已验证部分 | 每 Wave/每 5 原子任务写 `handoff.md` 检查点(已完成+证据/未完成/阻塞)   |
+| 把"已返回"当"已完成"   | 长任务部分失败却报全绿,未验证项被隐藏     | 可靠交付物四要素全披露,未验证项/部分失败明细不得省略                    |
+| 性能优化无基线声称提升 | 只说"快了"却无 profiler/benchmark 对比     | 先跑基线再跑对比,量化前后耗时/内存差异(`performance-benchmark`)       |
+| 审查请求却直接改实现   | 用户要 review,却直接重写代码              | 代码审查以发现问题为主,不默认重写;确认后再改(`code-review` 互斥规则) |
+| 数据库写入直拼 SQL     | SQL 注入风险,且绕过框架访问层             | 全部参数绑定,`IN(?,?,?)`/白名单映射 ORDER BY(`mysql-database`)        |
+
+## 六、边界外请求
+
+> **本节要点**:不覆盖领域(调研/排期/容器深度编排/IDE 配置等)只做边界说明,不替代专门流程。
+
+本技能不覆盖以下领域(详见 SKILL.md「本技能边界声明」章节):用户调研、工时排期、容器深度编排、IDE 配置、缺陷跟踪流程、安全深度扫描。出现这些请求时只做边界说明,不声称已覆盖,不尝试用泛化方案替代专门流程。
+
+## 七、显式调用与自动路由
+
+> **本节要点**:`@` 显式调用只跳路由、不破安全闸门;专项 reference 不支持 `@`;含知识图谱用法 Q&A。
+
+**Q:显式调用会破坏自动识别吗?**
+A:不会。显式调用是前置短路逻辑:仅当输入以 `@<合法英文标识>` 开头时跳过关键词路由,否则完全走原有路由表 + 优先级矩阵 + 意图三分法。拼写错误、邮箱、@提及等不触发显式调用,自动回退到关键词匹配。
+
+**Q:显式调用后还能加载协同 reference 吗?**
+A:可以。显式调用只跳过"路由匹配"环节,领域路由表的协同 reference 仍按需加载。例如 `@api-design` 仍会按需加载 `frontend-design`、`cms-development`。
+
+**Q:显式调用后,意图三分法和安全闸门还生效吗?**
+A:全部生效。显式调用只决定"用哪个子技能",不决定"怎么执行"。意图三分法、安全闸门、澄清策略、验证闭环、复盘沉淀全部照常执行。
+
+**Q:显式调用后任务-技能不匹配,提示几次?**
+A:最多 1 次。用户确认后立即执行,不再追问;用户回复"按原指定执行"时按显式指定走。避免过度澄清(详见 SKILL.md「对话流异常处理边界 → 澄清过度」)。
+
+**Q:为什么 `@laravel-development` / `@java-development` 不生效?**
+A:Laravel、Java(及 laravel-testing)属于「专项 reference 映射」,只作领域协同资料按需加载,**不加入 `@英文标识` 显式调用索引、也不计入 19 个子技能数量**(见 SKILL.md「专项 reference 映射」)。要用它们,在输入里用关键词触发领域路由即可自动追加其框架约定(如"Laravel/Eloquent/Blade 写代码"→ 代码生成 + laravel-development 协同;"Java/Spring Boot 写代码"→ 代码生成 + java-development 协同)。只有 19 个核心子技能支持 `@` 显式调用(含新增的 `@project-knowledge-graph`)。
+
+**Q:项目知识图谱(`@project-knowledge-graph`)怎么用?构建和查询耗 token 吗?**
+A:**零 LLM token**。构建走 `scripts/build_graph.py` 纯正则静态抽取(无 AST/tree-sitter;LSP 仅探测 `intelephense`/`phpactor` 可用性并写 `meta.json.lsp_available`,**不参与抽取**),不进 LLM 上下文(构建 token≈0);查询只返回子图 JSON(节点硬上限 `MAX_NODES=80`),也不重读源码。用法:
+
+- 构建:`python scripts/build_graph.py --root {PROJECT_ROOT} --rebuild`
+- 查依赖闭包(跨模块改动/重构前):`--query a.php,b.php,c.php --direction both --depth 3`(**多个入口逗号分隔,自动合并子图**——覆盖 SKILL.md Step2 P1 的依赖闭包用法)
+- 查上游依赖方:`--query 某模块 --direction up --depth 2`
+- 图不存在/被误删:自动触发 `[GRAPH-STALE]` 当场重建,不会崩(不会报 `GRAPH-MISSING`,也不会"回退全图扫描"——未命中符号时返回空子图)
+- 真实边类型:`include`(含 require/include_once/require_once) / `use`(命名空间) / `autoload`(new \Ns\Class) / `extends` / `template`({include file=}) / `tpimport`(ThinkPHP import/vendor/Loader::import) / `import`(非 PHP 语言) / `cssimport` / `asset`(HTML link/script) / `calls`。注意 `require` 已并入 `include`,`implements` 是类属性、**非独立边**。token 节省量级见 README「附录 A」。
+
+## 八、子Agent 边界
+
+> **本节要点**:子 Agent 一律禁改码,只检索收集;生成式方案须主 Agent 复核后才落地。
+
+**Q:子 Agent(如 code-explorer)能帮我直接改代码吗?**
+A:不能。无论何种子 Agent,一律禁止写或改项目代码文件(L0 级硬约束),写码/改码须主 Agent 自写或经显式授权并复核边界后执行。详见 `SKILL.md → 二、子Agent 边界与委派协议`:检索收集型子 Agent 只返回 `文件:行号 + 原文`,禁推理归纳;审查型只输出问题清单;子 Agent 不继承版本/环境/用户有意优化等铁律,其生成式结论须主 Agent 复核。委派前主 Agent 必须先给出目标/边界/验收口径,否则触发"理解委派"失败模式收回任务。
+
+**Q:子 Agent 返回的"修复建议"能直接用吗?**
+A:不能。检索收集型子 Agent 只能给原文,审查型可给问题清单但仍需主 Agent 复核;任何生成式修改方案都必须经主 Agent 审查边界、跑验证后才能落地(呼应安全闸门与红线6)。
+
+**Q:什么情况下才能委派子 Agent?**
+A:主 Agent 先完成需求分析与意图澄清,输出目标/边界/验收口径后,再把执行型子任务委派出去(见 `task-decomposition-and-execution.md` 第四步「委派简报」)。任务未明确即委派会触发"理解委派"失败模式,收回任务回到需求分析。
+
+## 九、长任务执行与可靠产出物
+
+> **本节要点**:Wave 轮次不跨累计;handoff 检查点续做;部分失败不谎报全绿;自动执行须开放权限。
+
+**Q:长任务会不会因为"轮次上限"被中途强制结束、之前做的工作全废?**
+A:不会。"安全最大轮次 = 6"只约束**单个 Wave 内的收敛循环**,不跨 Wave 累计。大任务应通过 Wave 拆分 + 跨会话 `handoff.md` 续做;已完成 Wave 的产出物(变更文件 + 验证证据)保留,不回退清零。达到上限时输出该 Wave 未完成清单并转交续做。
+
+**Q:长任务跑到一半、对话被压缩或我关掉 IDE,进度会丢吗?**
+A:不会丢。每完成一个 Wave 或累计 5 个原子任务,必须向 `.ai-memory/handoff.md`(跨会话可迁移)或本任务状态文件落检查点,含「已完成项(带验证证据) → 相关文件 + 未完成项(下一步) + 阻塞项」。新会话/新电脑读取 `handoff.md` 即可无损续做,不重跑已验证部分(见 `SKILL.md → 长任务执行可靠性` 与 `project-memory-management.md` 交接协议)。
+
+**Q:长任务部分批次失败了,会不会被说成"全部完成"?**
+A:不会。可靠交付物强制四要素:① 已完成清单(每项带验证证据) ② 未验证项/已知限制(显式列出,禁止隐藏) ③ 部分失败明细(哪些批次成功/失败、是否可 resume、失败原因与已落地错误列表) ④ 下一步(未完成项续做动作)。任何"已返回结果"不等同"已验证完成";未声明的验证视为未完成。
+
+**Q:被设计的长任务接口(Init-Step-Poll)和我让 Agent 跑的长任务,是一回事吗?**
+A:不是两层。前者是你**产品的**后台长任务(批量导入/导出/生成静态页等)须采用 `Init → Step → Poll` 防卡死架构,权威定义与「长任务验证」证据表见 `software-project.md` 第六步;后者是 **Agent 自身**的长链路执行须落检查点、披露未验证项。两层都要求可验证进度与恢复点,但约束对象不同(产品侧另见 `cms-development.md` 第六步、`api-design.md`、`task-decomposition-and-execution.md` 第四步 Wave 拆解与上下文隔离)。
+
+**Q:长任务怎么才算"验收通过"?**
+A:除通用验收标准(见 `SKILL.md` 第五步)外,长任务还须满足「长任务可靠」维度:有 handoff/状态文件检查点;交付含未验证项与部分失败明细且未隐藏;被设计的系统层 Init/Step/Poll/Cancel/失败重试/僵尸检测各有验证证据(见 `software-project.md` 第六步「长任务验证」证据表)。
+
+**Q:长任务要"自动执行 + 自动测试",为什么还得在运行时开放权限?**
+A:捕获 hook(capture_learning.py 等)和长任务闭环要自主跑通「执行命令 → 跑测试(php -l / phpunit / node --check)→ 落记录」,依赖运行时授予 `Bash`/`execute_command` 等工具的**免确认执行权限**。系统默认对危险操作(如**删除文件**、写入受保护目录、生产配置)会要求用户确认——若未开放,长任务每步弹确认就会被卡死,自我改进闭环也无法自主推进。开放方式两层:① 技能侧 `SKILL.md` 的 `allowed-tools` 已声明需要 `Bash` 等工具;② 运行侧(IDE/平台权限策略)须允许这些工具 auto-approve(或任务前显式授权删除/写入等)。未开放时,长任务须退化为「每步请求确认」模式,不得假设可无人值守自动执行(见 `SKILL.md` frontmatter 的 `allowed-tools` 声明 与 `FAQ.md` 第九节)。
+
+## 十、数据库与迁移安全
+
+> **本节要点**:SQL 全参数绑定、UPDATE/DELETE 带 WHERE+先 SELECT、DDL 给回滚;慢查询先 EXPLAIN 再优化。
+
+**Q:写 SQL / 做数据库迁移前要注意什么?**
+A:遵循 `mysql-database` 规范:① 所有外部输入必须参数绑定(禁直拼 `$id`/`$kw`,用 `?` / `IN(?,?,?)` / 白名单映射 ORDER BY / LIMIT 转 int);② 任何 UPDATE/DELETE 必须带 WHERE 并先 SELECT 验证影响行数,禁无 WHERE 写操作;③ 建表统一 `utf8mb4`、InnoDB、`BIGINT UNSIGNED` 主键,金额用 `DECIMAL` 禁 float;④ 索引必须对应具体查询路径并用 `EXPLAIN` 验证 `key/type/rows/Extra`,禁无理由加索引、禁低区分度字段单独建索引;⑤ DDL/数据修复必须给出备份方案 + 执行 SQL + 影响行数预估 + 回滚 SQL + 执行窗口,大表 ALTER 须在线 DDL(见 `mysql-database` 第八步)。
+
+**Q:慢查询怎么诊断,能直接上缓存吗?**
+A:先拿证据再优化:原始 SQL + 参数样例 + 表行数 + `EXPLAIN` + 相关索引。看 `type`(警惕 ALL/index)、`rows`(远大于返回行数)、`key`(未命中预期)、`Extra`(Using filesort/temporary)。优化顺序:改查询条件 → 调索引 → 改分页/统计 → 拆表/缓存;**不得先上缓存掩盖 SQL 问题**。
+
+**Q:大批量导入/导出/修数据为什么要用 Init-Step-Poll?**
+A:单事务影响行数超 10000 必须分批;大批量任务须采用 `Init → Step → Poll` 渐进式防卡死架构(Init 创建、Step 分批、Poll 查进度、支持 Cancel/失败重试/僵尸检测),避免长事务锁表、内存溢出或前端超时卡死(见 `api-design` 长任务模式与 `mysql-database` 第六步)。
+
+## 十一、专项领域与框架边界
+
+> **本节要点**:CMS 先确认类型再生成;Laravel/Java 只协同、不 `@` 显式调用(未确认前不套用到非框架项目)。
+
+**Q:CMS 二次开发(帝国CMS/WordPress/PHP8)要注意什么?**
+A:遵循 `cms-development`:先确认 CMS 类型再生成框架特定代码(未确认前禁止生成);PHP8 兼容(数组键引号、短标签、`each/create_function`、`??` 优先级、`fetch1` 返 false 守卫等);严守安全红线(SQL 注入/XSS/CSRF/文件上传/反序列化/路径遍历/eval/Include);插件走官方扩展点,后台路径不写死;CMS 数据库任务联动 `mysql-database` 的表前缀与官方数据访问层。
+
+**Q:我的项目是 Laravel/Java,能用 `@laravel-development` 显式调用吗?**
+A:不能。Laravel/Java(及 laravel-testing)属于「专项 reference 映射」,只作领域协同资料,不进 `@` 显式调用索引、不计入 19 子技能(见 七、Q5)。用关键词触发领域路由即可自动追加其框架约定;但**未确认 Laravel/Java 项目前,不得把规则套用到 CMS 等非框架项目**(领域路由边界)。
+
+## 十二、新会话恢复与项目记忆
+
+> **本节要点**:记忆只写 `.ai-memory/` 不污染仓库;新会话自动恢复;daily 即时追加、超 30 天蒸馏。
+
+**Q:新会话/换电脑怎么接着上次做?**
+A:新会话首次响应前执行 Step 1.1「恢复历史记忆」:探测 `{PROJECT_ROOT}/.ai-memory/`,按三层策略加载(Metadata→Body→References,token 预算≈1000);你说"继续/下一步"时先加载上次主线目标、执行位置、阻塞状态再动手,不重跑已验证部分。长任务续做读 `.ai-memory/handoff.md` 检查点(见 九、长任务)。无历史记忆时正常进意图识别,不阻塞(详见 SKILL.md Step 1.1 与 `project-memory-management`)。
+
+**Q:项目记忆存在哪、会污染代码仓库吗?**
+A:只写 `.ai-memory/`(会话摘要、决策记录、规范捕获、已知问题清单),业务历史写 `project_memory.md`;**不写入代码仓库、不污染版本控制**。跨会话恢复、决策可撤销性、规范复用都靠它(见 `project-memory-management`)。
+
+**Q:daily.md 是什么?和 session_memory 有什么区别?**
+A:`daily.md` 是当日逐条操作日志(append-only),每次完成实质性工作后**立即追加**,记录单条操作的摘要、修改文件、决策和验证结果。`session_memory` 是会话结束后生成的完整总结,包含全局目标、已完成清单、关键决策和下一步计划。两者互补:`daily.md` 记录操作点滴(操作级),`session_memory` 记录会话全貌(会话级)。
+
+**Q:写后即记协议什么时候触发?**
+A:7 类实质性操作完成后自动触发,不经确认:Bug 修复、功能实现/代码生成、代码审查/重构、技术选型定案、配置变更/数据库迁移、文档生成/规范沉淀、项目约定/用户偏好新发现。纯信息查询、只读检查、临时测试不触发(详见 SKILL.md Step 0)。
+
+**Q:旧日志怎么清理?**
+A:超过 30 天的 `{YYYYMMDD}/` 日志目录在 Step 6 复盘时触发蒸馏:关键决策/Bug 模式/项目约定 → 提炼入 `project_memory.md`,纯操作流水丢弃,蒸馏后删除整个日期目录。`daily.md` 当日超 8000 字符出示精简提醒,超 12000 字符强制蒸馏(详见 `project-memory-management` 记忆维护协议)。
+
+## 十三、文档与规范类任务(doc-generation)
+
+> **本节要点**:文档不得替代验证、签名须对齐真码;Spec/选型须先验证再拍板。
+
+**Q:让我写 API 文档 / README / 部署手册,是不是写完就行?**
+A:不是。文档、注释、规范属于"代码可读性的延伸",**不得替代代码验证**。写文档前须先确认对应代码已实现并跑通(至少 `lint` + 关键路径自测);文档里的接口签名、参数、返回值必须与真实代码一致,禁止凭假设补文档。文档发布须带回滚方案与发布/运维步骤(详见 `doc-generation`:文档不得替代验证、发布须含回滚与运维步骤)。
+
+**Q:先写 Spec(需求/设计文档)再写代码,Spec 本身需要验证吗?**
+A:需要。Spec 驱动开发走 `spec-driven-development` 五步(意图→边界案例→Requirements→Design→Tasks),编码前须过「第六步:编码前 Spec 验证」:逐条 Requirements/Scenario 与真实代码/接口 trace 核对,确认可实现、无歧义才进编码。Spec 与实现脱节(写完 Spec 不校验就编码)会在后期返工,且违反 Step 4 验证前提。
+
+**Q:技术选型(该用哪个框架/库)能直接拍板吗?**
+A:不能。涉及技术选型须走 `tech-selection`:先列候选与决策维度(团队/生态/长期维护/迁移成本),给出可撤销建议并说明假设,而非凭偏好下结论;选型结论须记录到项目记忆的 Decision Record 以便回滚(见 十二、与 `tech-selection`)。
+
+## 十四、JavaScript / Node.js 专项(javascript-development)
+
+> **本节要点**:JS 走 node --check 四步;PHP 内联 JS 须额外三道校验(`php -l` 对 JS 无感)。
+
+**Q:JS/Node.js 项目能用 `@javascript-development` 显式调用吗?**
+A:不能。`javascript-development` 与 Laravel、Java 同属「专项 reference 映射」,只作领域协同资料按需加载,不进 `@` 显式调用索引、不计入 19 子技能(见 七、Q5)。命中 JavaScript/Node.js/ES6/JS 代码检查/JS 风格规范/PHP 内联 JS 等关键词时自动加载。
+
+**Q:JS 项目代码质量怎么验?**
+A:走 `javascript-development`「代码质量检查流程」4 步:① 确认 Node.js 运行时版本(需 >= 15,推荐 18+)→ ② `node --check` 语法检查(利用 Node.js 内置语法解析器,不执行代码)→ ③ 全角符号自动修复(ESLint `--fix` 或按映射手动替换)→ ④ `node -e` 可执行性验证(验证修复后脚本可被 Node.js 正常加载并运行)。验证后再用 `code-review` 加载 JS 代码风格规范 47 条规则做静态审查。Node.js 版本未达 15 必须升级或在 README/CLAUDE.md 标注兼容范围。⚠️ 若 JS 写在 **PHP 文件内联**(混 `<?php ?>` 与 HTML、`onclick="..."` 事件属性、`window.open` 弹窗居中),`php -l` 对 JS 完全无感,必须额外走 `javascript-development`「CMS / PHP 内联 JS」三道强制校验:HTML 事件属性引号配对、window.open features 串非空收尾、批量改造后全仓逐条 Node 校验。
+
+**Q:JS 风格走哪个规范?**
+A:默认走 `javascript-development`「JS 代码风格规范」节,47 条规则 8 分类,按优先级 CRITICAL > HIGH > MEDIUM > LOW:Module/Language/Type Safety/Naming/Control Flow/Functions/Objects/Formatting。核心约束:const 优先(永远不 var)、ES module 优先(不用 require)、class 优先于原型操作、严格相等(==/!= 仅用于 null 检查)、显式分号、不修改内置原型、不用 eval。项目特殊规范存在时以项目为准,但禁止与 CRITICAL 级冲突。
+
+**Q:JS 代码有全角符号/中文标点怎么修复?**
+A:走 `javascript-development`「代码质量检查流程」第 3 步:全角符号修复用 ESLint `--fix` 自动修复,或手动将全角符号替换为半角(`,`→`,`、`。`→`;`、`!`→`!`、`?`→`?`、`:`→`:`、`「`/`」`→`"`、`(`/`)`→`(`/`)`)。常因来源内容含中文标点、复制粘贴编码异常触发。修复后必须重新跑 `node --check` 验证语法,再执行确认逻辑未改坏。
+
+## 十五、Hooks 自动守卫(hooks.json)
+
+> **本节要点**:15 个可选护栏;两种启用;hooks 不能替代 Agent 验证(动态/业务正确性仍须显式证据)。
+
+**Q:技能自带的 hooks 是什么,怎么启用?**
+A:本技能在 `hooks.json` 中声明了一组**通用示例**守卫钩子(共 15 个:PreToolUse 3 + PostToolUse 11 + PreCompact 1),覆盖写前/写后/压缩三类时机,对全部子技能与 JS 专项共用。启用方式:把 `hooks.json` 接入你的 Agent 运行时(具体见各 IDE 的 hooks 配置入口),并设置两个环境变量——`PYTHON_BIN`(Python 解释器路径)与 `HOOKS_DIR`(hook 脚本目录,可指向技能自带 `hooks/` 或你项目的 `.codebuddy/hooks/`)。`hooks.json` 内命令串用 `{{PYTHON_BIN}} "{{HOOKS_DIR}}/xxx.py"` 占位符,加载时由运行时替换为真实路径,不写死任何机器路径。
+
+**Q:hooks 守卫具体做哪些检查?**
+A:共 15 个钩子,分三组:
+
+- **PreToolUse(写前,3 个)**:`backup_on_write`(写/改已存在源码前自动备份为 `.bak`,保留最近 3 个历史版本 `.bak` / `.bak.1` / `.bak.2`)、`guard_dirs`(拦截对数据/依赖目录的写操作)、`plan_guard`(改核心目录前强制要求存在进行中的 `*_plan.md`)。
+- **PostToolUse(写后,11 个)**:`lint_on_write`(写 PHP 后语法自检)、`php8_compat`(PHP 8.x 兼容扫描)、`security_scan`(安全红线静态扫描,对齐 Rules §5)、`secret_scan`(敏感信息扫描,对齐 §5/§19)、`utf8_check`(UTF-8 合法性校验)、`debug_residue`(调试残留扫描)、`select_star`(裸 `SELECT *` 扫描)、`dep_scan`(依赖漏洞扫描提示)、`sql_injection_check`(SQL 注入检测,覆盖 PHP/Python/JS/Java 拼接模式)、`cms_risk_check`(CMS/框架风险检测,覆盖 18 种 CMS/框架 SQL 注入 + 13 种框架特有风险)、`capture_learning`(学习捕获:监听工具输出错误信号与用户输入纠错句式,命中即回显「建议记录到 error-ledger」提醒,非阻塞、不自动写文件,与前面 10 个防护型 hook 互补——详见 `error-ledger.md`)。
+- **PreCompact(压缩前,1 个)**:`handoff_snapshot`(压缩/交接前生成检查点快照,保障长任务可恢复)。
+
+**Q:hooks 能替代 Agent 自己的验证吗?**
+A:不能。hooks 是**强制护栏**,只兜底机械项(备份、lint、PHP8 兼容、安全/脱敏、UTF-8、调试残留、SELECT \*、压缩快照、规划拦截、受保护目录写前拦截);动态/业务正确性(如真实运行、端到端验证)仍须 Agent 显式产出证据链。hooks 拦截即视为该防线未过,禁止绕过。若运行时无 hooks 集成,须回退到对应正文手动执行,不得留空白洞。
+
+**Q:脚本路径报"找不到文件"怎么办?**
+A:先确认 `HOOKS_DIR` 指向的目录里确有对应 `.py`(技能自带在 `dev-expert/hooks/`,或你复制到项目的 `.codebuddy/hooks/`)。示例 `hooks.json` 默认指向占位符,未设 `HOOKS_DIR` 或指向错误目录都会找不到脚本——这不是技能 bug,是部署变量未配置。
+
+**Q:运行时不支持 `{{PYTHON_BIN}}` / `{{HOOKS_DIR}}` 占位符替换怎么办?**
+A:用安装器预处理:运行 `python hooks/install_hooks.py`(自动探测 Python 路径和 hooks 目录),生成已替换占位符的 `hooks.installed.json`,直接接入运行时即可。支持 `--python-bin` / `--hooks-dir` 参数或 `PYTHON_BIN` / `HOOKS_DIR` 环境变量指定路径;`--in-place` 可原地覆盖(自动备份 `.bak`)。详见 README「方式 B:安装器预处理」。
+
+**Q:不想用 hooks 了,怎么禁用 / 卸载?**
+A:两种方式,按你想禁的范围选:① 运行时侧:在 IDE 的 hooks 配置里移除对 `hooks.json` / `hooks.installed.json` 的引用即可,脚本本身不动、下次想用再接回;② 脚本侧:删除 / 移走 `HOOKS_DIR` 指向目录里的对应 `.py`(只想禁某一项就删单个文件,如 `backup_on_write.py`)。hooks 是**可选增强**,不接也不影响子技能本身运行,只是少了机械护栏(备份 / lint / 安全扫描等)兜底——禁用后须自行用 `php -l`、人工审查等手段补上验证(见本节约上文「hooks 能替代吗」)。重装:重跑安装器 `python hooks/install_hooks.py` 或把 `hooks.json` 重新接入运行时。
+
+**Q:`hooks.capture-draft.json` 和 `hooks.installed.json` 为什么是两个分开的文件,分别干嘛?**
+A:两者类别和用途都不同,必须分开:
+
+- **`hooks.capture-draft.json`(增量草案,源文件)**:单独放着"捕获 hook"的额外一段——它比 `hooks.json` 多了**一条 `UserPromptSubmit` 事件**(命令带 `--mode correction`,直接抓取你输入里的纠错句式,是信号最准的来源)。但 `UserPromptSubmit` **不是所有 IDE 都支持**,若写进主 `hooks.json`、在不支持的 IDE 上会因为"未知事件名"导致整份配置加载失败,所以这条只能**单列 opt-in**:你想用就单独把这段并入,不用就不影响主配置。它的 PostToolUse 项与 `hooks.json` 里的是重复的,真正多出来的是 `UserPromptSubmit` 那一段。
+- **`hooks.installed.json`(安装产物,自动生成)**:是 `install_hooks.py` 把 `hooks.json`(或 `capture-draft.json`)里的 `{{PYTHON_BIN}}`/`{{HOOKS_DIR}}` 占位符替换成真实机器路径后的输出文件,可直接接入运行时。它和"源文件"是天然两类——合并了就又变回需要替换占位符的源状态了,所以不可能和源文件合二为一。
+- 三者关系:`.json` = 手写主源、`capture-draft.json` = 可选附加源(多一路最准的纠错捕获)、`.installed.json` = 编译产物。日常只维护前两个源,第三个由安装器重跑覆盖(详见 README「三个 hooks 文件的关系」小节)。
+
+## 十六、需求澄清与模块边界
+
+> **本节要点**:复杂任务强制澄清(≤3 问);模糊词走 Wayfinder;"目标+约束+验收"最省追问。
+
+**Q:需求澄清门控什么时候触发?**
+A:当意图三分法判定为「复杂任务」且满足任一触发信号(见 SKILL.md Step 1.5)时强制进入。信息查询和简单任务跳过门控。目的是把模糊形容词转为可验收标准,避免做完后发现理解不一致。
+
+**Q:门控会问很多问题吗?**
+A:最多 3 问——只问影响验收口径的关键缺口。假设能合理推断的用默认值并标注,让用户纠正而非从头问起。
+
+**Q:大型模糊任务(如"帮我优化系统")怎么处理?**
+A:启动 Wayfinder 模式:先做现状快照→问题树展开→候选路径排序→最小探索动作→收敛为明确任务。不直接用"优化"这种模糊词进入实现。
+
+**Q:模块边界审查什么时候做?**
+A:在 software-project 第三步(架构方案)和第四步(Wave 拆解)之间。检查依赖方向/接口契约/数据持有/变更半径/术语一致性。循环依赖或数据归属冲突为阻塞级,必须在拆解前解决。
+
+**Q:代码生成怎么会联动测试和审查?**
+A:复杂任务(多模块/数据持久化/安全敏感)在代码生成第〇步自动决策是否联动 test-generation 和 code-review。单文件简单任务不联动,避免过度流程。
+
+**Q:术语表(Glossary)是什么?**
+A:当同一概念被多个名称指代时,project-memory-management 会自动记录规范名称到 `.ai-memory/project_memory.md` 的 Glossary 章节。后续会话恢复时会加载,确保 Agent 使用统一术语。新会话首次识别到术语漂移时会提醒用户。
+
+**Q:快速原型需要画图吗?**
+A:不需要高保真设计图。frontend-design 的二步点五用 ASCII 线框图快速对齐页面骨架和交互流。用户确认结构后再进入完整视觉设计,避免方向性返工。
+
+**Q:哪些"模糊地带"系统会停下来问我,而不是自己定?**
+A:典型模糊地带(命中即触发澄清,不会擅自动手):
+
+- **验收口径不确定**:如"做得好一点 / 快点 / 安全点"这类形容词,无量化标准 → 转成可验收标准再动手;
+- **多解分支**:实现路径有 ≥2 种且影响不同(如分页用游标 vs offset)→ 问你偏好;
+- **默认值有业务后果**:如"默认渠道 / 默认状态"选错会写错数据 → 高澄清级别先问;
+- **范围边界**:任务是否包含某模块/某依赖(如"重构这段"要不要连带改调用方)→ 确认变更半径。
+  反之,纯信息查询、单行修复、可局部回退且目标明确的任务不会问,直接进快速通道(见 SKILL.md「自主度分级」)。
+
+**Q:我不想被问太多,怎么一次性把信息给够?**
+A:用「目标 + 约束 + 验收」三段式描述,能大幅减少追问:
+
+- **目标**:要做什么(如"导出后台用户");
+- **约束**:范围 / 格式 / 权限 / 性能等硬限制(如"按筛选条件、CSV、需鉴权限频");
+- **验收**:怎么算做对了(如"能正确导出 1 万行且带表头")。
+  三要素越齐,门控问得越少;缺哪块系统就只补问哪块(最多 3 问),其余用合理默认并标注让你纠正。这正对应十六、Q2「假设能合理推断的用默认值并标注」。
+
+**Q:Wayfinder 探索和我直接给目标,区别在哪?**
+A:当你只给"优化系统 / 帮我看看"这类过泛词时,系统不会莽撞动手,而是先走 Wayfinder:现状快照 → 问题树 → 候选路径排序 → 最小探索 → 收敛成明确任务,过程中可能回问你方向。若你已能给出较具体目标,则跳过探索直接路由,省去来回。即:**越具体越省事,越模糊越要先对齐**(见 十六、Q3)。
+
+## 十七、交付保障与踩坑管理
+
+> **本节要点**:SELF-AUDIT 16 条、收尾报告 5 字段、Graceful Abort、错误册 ERR-XXX、学习捕获三层闭环。
+
+**Q:SELF-AUDIT 执行率自检是什么?什么时候触发?**
+A:交付前强制执行(收口于 Step 5),16 条硬约束(E2E 条目/验收标准/安全/质量/性能/GATE-CHECK/轮次/构建/修改 checkpoints/回退/规划/图谱 G1/G2'/G3/架构自检/证伪)。任一 ❌ 回退对应阶段修复→重走 Step 4→复检,最多 3 轮;超出后列未修复项+原因+选项,不阻塞交付。详见 `delivery-assurance.md`「执行率自检」。
+
+**Q:收尾报告长什么样?**
+A:固定 5 字段格式:`完成度: X% | 主线: {状态} | 产出: {摘要} | 待办: {内容/无} | 下一步: {建议/无}`。禁止自由格式,必须有验证可追溯。详见 `delivery-assurance.md`「收尾报告模板」。
+
+**Q:任务中途我想中止怎么办?**
+A:走 Graceful Abort 流程:输出中止声明→保留 `.bak` / `.bak.1` / `.bak.2` 备份→_plan.md 标注中断点→清理临时脚本(不删 .bak)→简化收尾。恢复时从 handoff.md 或 \_plan.md 中断点继续,回滚则倒序 `.bak` 恢复(`.bak` 为最新备份,`.bak.1` 次之,`.bak.2` 最旧)。详见 `delivery-assurance.md`「Graceful Abort」。
+
+**Q:确认超时了会怎么处理?**
+A:通用确认 5 分钟无回复→最保守/安全选项自动推进;破坏性操作(DDL/删除/覆盖/批量写)→默认取消;规划确认→默认推进;安全相关→一律等明确回复。详见 `delivery-assurance.md`「确认超时默认规则」。
+
+**Q:踩坑错误册(ERR-XXX)怎么用?**
+A:索引表 `error_index.md` + 单条 `errors/ERR-XXX.md`。Bug 诊断/审查/重构 session 启动时先用正则/关键词匹配索引的触发关键词,命中则读对应 ERR-ID 全文复用修复方式。新坑排查耗时 ≥ 40 分钟→强制分配 ERR-ID+写单条+更新索引+同步 project_memory Known Issues。详见 `error-ledger.md`。
+
+**Q:新加的「学习捕获」钩子是怎么让技能自我改进的?**
+A:分三层闭环,但本技能**不自动改写自身规则**(避免越权):① **捕获**(`capture_learning.py`,挂 PostToolUse):监听工具输出的错误信号(Parse error / PHP Fatal / 断言失败 / GATE-CHECK 不足等)+ 你输入里的纠错句式,命中即回显「建议记录到 error-ledger」提醒——非阻塞、`exit 0` 仅提醒、不自动写文件;② **沉淀**(`error-ledger.md`):你按提醒把坑记成 `errors/ERR-XXX.md`(含 `Recurrence-Count`/`Tasks` 等字段),形成可复用知识库;③ **提升**(`recurrence_promote.py`):解析单条错误册,当某坑 `Recurrence-Count≥3 且 Tasks≥2 且 30 天窗口`内反复出现,输出「建议升规则」提示——由你确认后才写入项目规则文件(用 `--target` 指定落点),不擅自改。简言之:**捕获提醒 → 你记录 → 高频坑经你确认升级为规则**,闭环靠人把关,Agent 不自我改规则(详见 `error-ledger.md` 与 `hooks/capture_learning.py` / `hooks/recurrence_promote.py`)。
+
+**Q:批量修改很多文件时有什么安全保障?**
+A:触发"批量修改 7 防线"(≥ 3 文件或 ≥ 10 处修改点):预检 lint→试点 3-5 文件→备份 `.bak`(保留最近 3 个历史版本)→逐文件执行+日志→后检 lint→MD5 对比→回读确认。ROLLBACK > 20% 整批回退。详见 `execution-safety.md`「批量修改 7 防线」。
+
+## 十八、子技能组合与触发进阶
+
+> **本节要点**:组合自动发生、不用手拼;正确姿势"先审查后重构"(审查只读);附一句话触发对照表。
+
+### 能力边界:多个子技能能不能同时用、怎么配合?
+
+> 先说清一件事:**组合是自动的,不用你手动编排。** 你只需把完整目标说清(如"实现导出功能并出接口文档"),系统按领域路由表 + 优先级矩阵自动串联所需子技能,你不必显式点名谁先谁后。下面内容仅供你想了解内部协作方式、或在自动结果不符预期时手动微调。
+
+**Q:我想"代码审查 + 重构"一起做,可以吗?**
+A:可以组合,但正确姿势是「先审查、后重构」,不是在同一份代码上同时改。这通常是**系统自动按"治理阶段"路径串联**的,你也可以显式用 `@code-review` + `@refactoring` 精确控制:
+
+- 代码审查(`@code-review`)按「审计修复分离」规则是**只读**的:只输出分级问题清单(Bug / 安全 / 性能 / 坏味道)和修复建议,**不默认改写实现**(见 SKILL.md 代码审查 协同 `refactoring` 的边界注记"以发现问题为主,不默认重写实现")。
+- 重构建议(`@refactoring`)负责把审查发现的问题落地为具体重构步骤;它同样要求"未建立验证路径前不得扩大重构范围"。
+- 推荐配合链路:先 `@code-review` 出报告 → 你确认要修哪些 → 再 `@refactoring`(或代码生成)按报告逐条改 → 改完可再跑一次 `@code-review` 验收。这正是 README「协同技能」里的"治理阶段:Bug诊断/重构建议 → 代码审查 → …"路径。
+- 注意互斥提示:如果你 `@code-review` 却说"帮我重写/实现这段",系统会提示"任务更像代码生成,是否切换?"——因为审查 ≠ 改写。要改写必须走重构/生成分支并先确认(见 一、Q2 的任务-技能不匹配提示表)。
+
+**Q:还有哪些常见的"组合套餐"?**
+A:这些都是**系统自动触发**的例子(你一句话说清目标即可,无需手动拼)。完整「一句话触发对照表」(写功能+测试 / 修报错+审查 / 先定位瓶颈再重构 / 审查完再重构 / 写功能+顺带文档 等高频组合)见 README「一句话触发对照表」,系统按优先级矩阵自动串联,无需你手动拼。
+
+**Q:组合时会不会超出 reference 加载上限?**
+A:会受"单次最多加载 3 个 reference"约束(SKILL.md 对话流异常处理边界 → 过度路由)。当一句话挂了过多子技能时,系统回到意图三分法,只保留首选 + 必要协同,其余提示你分阶段做,不会一股脑全加载。
+
+### 触发方式:不知道功能名字 / 想组合两个,怎么说才命中?
+
+**Q:我想要某个能力,但不知道它叫"代码审查"还是别的名字,怎么说?**
+A:直接**描述目标/动作**,不必背名字。路由表同时匹配「子技能名」和「功能说明里的场景词」:
+
+- "帮我看看这段代码有没有问题 / 安不安全" → 命中 代码审查(无需知道 `@code-review` 这个名字)
+- "这段逻辑太绕了,理一理结构" → 命中 重构建议
+- "写个能跑的接口,最好带参数校验" → 命中 代码生成
+- 实在不确定,用最上层的 `@<英文标识>` 显式指定(见 七、);拿不准标识就直接描述,系统按关键词 + 意图三分法路由,命中不了会向你确认意图(不会乱猜)。
+
+**Q:我想同时用两个功能,怎么一句话表达才不跑偏?**
+A:两种稳妥表达:
+
+1. **顺带式**:"做 A,并 / 顺带 / 再帮我做 B"——如"帮我实现导出功能,顺带生成接口文档"。系统按优先级矩阵组合路由(A 为主、B 为协同)。
+2. **显式点名式**:用多个 `@` 标识,如"`@code-review` 先审一遍,`@refactoring` 再按问题重构"。显式调用只跳过路由匹配、不影响协同加载(见 七、Q2)。
+
+- 反例(易跑偏):只说"帮我优化一下"这类模糊词 → 触发 Wayfinder 探索而不是直接动手;想精准组合就用上面两种明确表达。
+- 若两个功能明显冲突(如"审查但不许改" vs "直接重写"),系统会按「任务-技能不匹配提示」表问你确认(见 一、Q2 / SKILL.md Step 1.2),不会擅自二选一。
+
+### 常见模糊需求 → 会命中哪个子技能(速查,专治"怕说错")
+
+不知道功能叫啥、怕描述模糊触发错?下表把**口语化模糊需求**直接映射到子技能,照着说基本不会偏:
+
+| 你说的(模糊表达)                      | 实际命中子技能            | 备注                             |
+| --------------------------------------- | ------------------------- | -------------------------------- |
+| "帮我看看这段代码有没有问题 / 安不安全" | 代码审查                  | 无需知道 `@code-review` 这个名字 |
+| "这段逻辑太绕了,理一理结构"            | 重构建议                  |                                  |
+| "写个能跑的接口,最好带参数校验"        | 代码生成                  |                                  |
+| "这个报错帮我修一下"                    | Bug诊断                   |                                  |
+| "帮我写份 API 文档 / README"            | doc-generation            | 文档签名须与真实代码对齐         |
+| "给我做个网站 / 落地页"                 | frontend-design           |                                  |
+| "数据库慢查询怎么优化"                  | mysql-database            | 常与性能基准测试协同             |
+| "CMS 二次开发(帝国/WordPress/PHP8)"   | cms-development           | 先确认 CMS 类型再生成            |
+| "技术选型用哪个框架/库"                 | tech-selection            | 不凭偏好拍板,给可撤销建议       |
+| "长任务 / 导入导出接口怎么设计"         | api-design                | Init→Step→Poll 防卡死            |
+| "让我做个 PPT / Word 报告"              | 依 docx/pptx 等 reference | 关键词触发                       |
+| "帮我管一下项目记忆 / 交接"             | project-memory            |                                  |
+
+**Q:我怕说错导致选错技能,怎么办?**
+A:三点打消顾虑,基本"选错无后果":
+
+1. **路由表同时认"名字"和"场景词"**:描述目标比背名字更稳;真命中不了,系统会向你确认意图,不会乱猜(见 一、Q1)。
+2. **显式指定错了也会被拦**:即使你 `@xxx` 指定错,系统检测到强冲突会提示"任务-技能不匹配",你回复后切换,绝不擅自二选一;回复"按原指定执行"则立刻按你指定的走(见 一、Q2)。
+3. **组合类用"做 A,顺带 B"句式**:A 为主、B 为协同,不必精确排序(见上「触发方式」两种稳妥表达)。
+   结论:**怕选错就描述目标,系统要么命中、要么问你一句、要么提示冲突——不会偷偷用错技能干完。**
+
+### 实战演练:一个"需要澄清 + 组合技能"的真实任务
+
+下面用一条完整链路演示系统如何走(你只需看"输入 → 系统行为",不必背流程):
+
+> **你的输入**:"我想给后台加个导出用户的功能,要快;顺带把接口文档也出了。"
+
+1. **路由**:关键词"导出 + 功能 + 后台" → 命中 `代码生成`(主)+ `cms-development` / `api-design`(协同)。「顺带出文档」触发 `doc-generation` 协同加载。
+2. **澄清门控触发**(因为是"写功能 + 涉及数据库/权限"的复杂任务,见 十六、Q1):最多问你 3 个关键缺口,例如:
+   - 导出范围:全部用户,还是按条件筛选?
+   - 输出格式:CSV / Excel / JSON?
+   - 是否要鉴权 + 限频(防批量拖库)?
+   - 能合理推断的(如"UTF-8 编码")用默认值并标注,不重复问。
+3. **你回答后进入实现**:先给方案(表/字段、接口签名、回滚路径)→ 你确认 → `代码生成` 落地 → `lint` 验证证据。
+4. **组合收尾**:`doc-generation` 产出的文档签名与你真实代码对齐,禁凭假设补接口(见 十三、Q1)。
+5. **交付**:收尾报告含完成度 + 验证证据(lint/运行输出),未验证项不隐藏(见 十七、Q2)。
+
+**要点**:你不需要背这套流程,只要把「目标 + 关键约束(范围/格式/权限)」说清,剩下的路由、门控、组合由系统按规则补齐。
+
+### 一句话触发对照表(深入用法速查)
+
+| 你想做的事        | 一句话怎么说(命中即组合)                       | 触发链路                          |
+| ----------------- | ------------------------------------------------ | --------------------------------- |
+| 写功能 + 顺带测试 | "实现登录接口,并生成单元测试"                   | 代码生成 + 测试用例生成           |
+| 修报错 + 顺带审查 | "定位这个报错,顺带看看附近有没有安全隐患"       | Bug诊断 + 代码审查                |
+| 先定位瓶颈再重构  | "这个列表很慢,先帮我定位瓶颈再重构"             | 性能基准测试 + 重构建议(先基线) |
+| 审查完再重构      | "`@code-review` 先审,`@refactoring` 再按问题改" | 代码审查(只读) → 重构建议         |
+| 写功能 + 顺带文档 | "加个导出功能,顺带生成接口文档"                 | 代码生成 + doc-generation         |
+
+## 十九、常见技能实际代码示例(可直接抄用)
+
+> **本节要点**:5 段可直接抄的真实代码(PDO 预处理 / PHP8 修复 / 审查报告 / 内联 JS 居中 / handoff 检查点)。
+
+上面各节偏步骤与模板;这里补几段**真实可运行**的代码片段,对应高频技能,照抄即可落地。每条都锚定前文规则出处。
+
+### A. 数据库写入:PDO 预处理(替代直拼 SQL)
+
+```php
+// ❌ 直拼(注入风险,违反 二、执行禁区)
+$id = $_GET['id'];
+$sql = "SELECT * FROM user WHERE id = $id";
+
+// ✅ 预处理(mysql-database 规范)
+$stmt = $pdo->prepare("SELECT * FROM user WHERE id = ?");
+$stmt->execute([$id]);
+$row = $stmt->fetch(PDO::FETCH_ASSOC);
+```
+
+> 补充:`IN` 用 `IN (?,?,?)` 占位;`ORDER BY` 走白名单映射;`LIMIT` 转 int;任何 UPDATE/DELETE 必须带 `WHERE` 并先 `SELECT` 验证影响行数(见 十、Q1)。
+
+### B. PHP8 兼容:两个高频修复
+
+```php
+// ❌ 数组键无引号 + fetch1 返 false 未守卫(PHP8 告警/致命)
+$row = $db->fetch1("SELECT * FROM news WHERE id=1");
+echo $row[id];          // Undefined array key(PHP8 报 warning)
+echo $row['title'];     // 若 $row 为 false → 致命错误
+
+// ✅ 加引号 + false 守卫(php8-compat)
+$row = $db->fetch1("SELECT * FROM news WHERE id=1") ?: [];
+echo $row['id'];
+echo $row['title'] ?? '';
+```
+
+> 其余 PHP8 项:短标签 `<?`→`<?php`、`each()`/`create_function()` 替换、`??` 优先级加括号、`class_exists()` 第二参数(见 十一、Q"CMS 二次开发")。
+
+### C. 代码审查(@code-review)只读报告长什么样
+
+```text
+[代码审查报告] src/Order.php
+- [Bug]     L42  未校验 $qty 上限,可传负数导致库存为负
+- [安全]    L88  直拼 SQL,存在注入(见 mysql-database)
+- [性能]    L120 循环内逐条 SELECT,建议批量 IN 查询
+- [坏味道]  L15  函数 80 行,建议拆 init / calc / final
+建议:先修 [安全][Bug],再据报告走 @refactoring。
+(本技能只出清单,不自动改写;确认后再改)
+```
+
+> 这正是 十八、所述「审计修复分离」:审查是只读的,要改写须走重构/生成分支并先确认。
+
+### D. PHP 内联 JS:window.open 弹窗居中(Node 校验 + 正确写法)
+
+```html
+<!-- ❌ features 以空串结尾 → 浏览器报 SyntaxError -->
+<button
+  onclick="window.open(u,'','width=600,height=400,left='+(screen.width-600)/2)"
+>
+  打开
+</button>
+
+<!-- ✅ 坐标用 Math.floor 包裹,末尾真实非空串 -->
+<button
+  onclick="window.open(u,'','width=600,height=400,left='+Math.floor((screen.width-600)/2)+',top='+Math.floor((screen.height-400)/2)+',scrollbars=auto,resizable=yes')"
+>
+  打开
+</button>
+```
+
+> 校验:`node --check` 对纯 JS 有效;**PHP 文件内联 JS,`php -l` 完全无感**,须把这段 JS 抽出来用 Node 单独跑(见 十四、Q2「CMS / PHP 内联 JS」)。
+
+### E. 长任务检查点(handoff.md 实际内容)
+
+```markdown
+# 交接快照 handoff.md
+
+## 已完成(带验证证据)
+
+- 导出接口 POST /api/export ✅ php -l 通过 + curl 返回 200 + 1 万行 CSV
+
+## 未完成
+
+- 导出限频未加(待你确认阈值)
+
+## 阻塞
+
+- 无
+
+## 下一步
+
+- 补限频 → 重跑 lint → 交付
+```
+
+> 每完成一个 Wave 或累计 5 个原子任务落一次,跨会话/换电脑读它即可无损续做(见 九、Q"进度会丢吗")。
+
+## 二十、新手高频疑问补遗(问答覆盖查漏)
+
+> **本节要点**:新手常问(纠正/回滚/中文/省 token/第一步/产出在哪),前面没落点的都收在这里。
+
+本节能级收口"前面章节没专门答、但新手常问"的问题,避免"有些疑问找不到答案"。
+
+**Q:它是不是理解错了我的需求?怎么纠正?**
+A:两种方式。① 即时纠正:直接说"不对,第 X 行应该 Y 而不是 Z"——系统按你的纠正重做并复跑验证,不辩解。② 若纠正涉及一个反复踩的坑,会触发"学习捕获"提醒你记入错误册(见 十七、Q"学习捕获钩子"),下次同类问题自动复用正确做法。纠正不会被当成"拒绝合作",放心提。
+
+**Q:它改错了 / 我想撤销某次改动,怎么回滚?**
+A:改动前 hooks 已自动备份为 `.bak`(`backup_on_write`,保留最近 3 个版本 `.bak`/`.bak.1`/`.bak.2`)。回滚:把 `.bak` 复制回原文件名即可恢复上一版,`.bak.1`/`.bak.2` 是更早版本。若任务进行中中止,走 Graceful Abort:`.bak` 保留、plan 标中断点,恢复时从 `handoff.md` 或 `.bak` 倒序恢复(见 十七、Q"任务中途想中止")。注意:`.bak` 是唯一的回滚点,**不要手动删 `.bak`**。
+
+**Q:支持中文吗?我的代码/注释是中文的。**
+A:支持。本技能面向中文开发者,输入用中文即可;代码注释、标识符、报错信息均按中文语境处理。唯一的编码红线是:**不要用 Shell 直接写中文/UTF-8 文件**(易乱码),交给 Edit/Write 工具或 Python `open(encoding='utf-8')` 处理(见 二、禁区 / 五、反模式)。
+
+**Q:我口头纠正它,算不算"反馈让它变好"?**
+A:算,但分两层。即时纠正只影响本次;若想"下次自动更准",需把坑记入错误册(ERR-XXX,见 十七、Q"踩坑错误册"),或经你确认后由 `recurrence_promote.py` 升为规则。单纯口头说一句不会自动改写技能自身规则——闭环靠人把关(详见 十七、Q"学习捕获钩子")。
+
+**Q:会不会很耗 token / 上下文?怎么省?**
+A:三道机制控成本:① 上下文延迟加载——只加载当前任务所需文件,不预读全库(见 SKILL.md「上下文延迟加载协议」);② 单次最多加载 3 个 reference,过度路由自动回退(见 三、/ 一、Q4);③ 单日日志超 8000 字符提醒精简、超 12000 强制蒸馏(见 十二、Q"旧日志怎么清理")。省 token 的实操:**需求一次说清(目标+约束+验收)**,少来回追问最划算。
+
+**Q:新手第一步该做什么?**
+A:不用学规则。① 打开 README「3 分钟上手」+「需求表达完整示例」,照着抄一句需求即可;② 不确定能不能做、会不会踩坑,先扫一眼 README「上手前先扫一眼红线」或 FAQ「能力边界速览」;③ 真遇到报错/卡住,去 FAQ「普通人可读版」按"你遇到的情况"对号入座。规则是给 Agent 看的,你只需把目标说清。
+
+**Q:它生成的东西在哪看?输出会跑哪里去?**
+A:两类产出。① 代码/文件类:直接写到你项目对应路径(改动前有 `.bak` 备份);② 报告/清单类:在对话里输出(如代码审查清单、审计结论、收尾报告)。每次交付含「变更摘要 + 验证证据 + 收尾报告」,你据收尾报告即可定位改了哪些文件(见 十七、Q"收尾报告长什么样")。

+ 369 - 0
.claude/skills/dev-expert/README.md

@@ -0,0 +1,369 @@
+# 编程专家 - dev-expert
+
+一个覆盖编程全生命周期的 AI 编程助手:说需求,它自动选对工具并组合执行,你不必记任何名字。
+
+```
+                    _ooOoo_
+                   o8888888o
+                   88" . "88
+                   (| -_- |)
+                   O\  =  /O
+                ____/`---'\____
+              .'  \\|     |//  `.
+             /  \\|||  :  |||//  \
+            /  _||||| -:- |||||-  \
+            |   | \\\  -  /// |   |
+            | \_|  ''\---/''  |   |
+            \  .-\__  `-`  ___/-. /
+          ___`. .'  /--.--\  `. . __
+       ."" '<  `.___\_<|>_/___.'  >'"".
+      | | :  `- \`.;`\ _ /`;.`/ - ` : | |
+      \  \ `-.   \_ __\ /__ _/   .-` /  /
+ =====`-.____`-.___\_____/___.-`____.-'=====
+                    `=---='
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+          佛祖保佑       永无BUG
+```
+
+> 🚀 **第一次用?只看这段**:直接说你的目标(如"帮我给后台加个导出按钮"),路由、组合、验证它全包了;只有"报错 / 不确定能不能做"时才需要查 FAQ。
+
+> 📌 **TL;DR(30 秒速览)**
+>
+> - **是什么**:19 个子技能的全链路编程助手;说需求即自动匹配 + 自动组合,无需记名字。
+> - **怎么用**:自然对话说目标(带"目标+约束+验收"最省事);边界/报错查 FAQ。
+> - **护栏**:hooks 是可选增强(备份/lint/安全);红线(直拼 SQL、盲写库、跳过验证)踩了被拦。
+> - **深入**:专项细节在 `references/*.md`,本文件只给入口与导航。
+> - **知识图谱**:`@project-knowledge-graph` 纯正则建依赖图,查询近零 LLM token(见附录 A)。
+
+### ⚠️注意
+
+> 本技能的目的是跨工具实现快速迁移,工具不好用就换!别想用工具黏上你的工作流和习惯!你可以随意的切换使用工具并保证你的进度保持同步不被任何工具绑定
+> 你的进度 规范 踩坑错误册 图谱 编排进度 都可以快速的随你的工具任意迁移而不被任何工具束缚!
+> hooks 和 scripts(知识图谱)属于高级进阶功能,须Agents根据自身规则适配才能使用,非开箱即用功能!
+
+### 为什么保留渐进式路由(而非一步直达)
+
+你可能会问:既然"说需求即自动匹配",为什么 SKILL.md 还要保留 `@显式调用 → 关键词 → 领域路由表 → 优先级矩阵 → 意图三分法` 这套多层、逐级收窄的**渐进式路由**,而不是直接让模型凭一句话判断进哪个子技能就执行?
+
+因为渐进式路由**不是单纯的"分类器",而是六步闭环工作流(Step 0–6)的入口分发轨道**——它的每一层输出都挂着后续的一道门。抛离路由等于拆掉轨道:模型仍会"做事",但流程会从中间断裂,丢失可验证、可恢复、可审计的保证。
+
+| 路由层                                       | 它挂接的后续步骤(不抛离的原因)                                                                   |
+| -------------------------------------------- | -------------------------------------------------------------------------------------------------- |
+| `@显式` / 关键词 / 领域路由表                | 命中结果决定加载哪个 `reference` 主模板,模板里的澄清门控、验证证据、SELF-AUDIT 清单才有挂载点     |
+| 领域路由表的「路由边界」列                   | 提前识别"不在本技能范围"的请求(用户调研 / 排期 / 容器编排 / 安全深度扫描),只做边界说明不硬覆盖  |
+| 优先级矩阵(40+ 行「X+Y」场景)              | 决定**组合协同顺序**(先审查后重构、先基线后对比、CMS 规范优先于代码生成),无此则组合失据         |
+| 意图三分法(信息查询 / 简单任务 / 复杂任务) | 决定走快速通道还是先方案确认——**安全闸门的触发点就在这里**,抛离会跳过生产配置 / DB 写入的事先确认 |
+
+如果完全抛离、改为一步直达,会同时丢掉四道关:Step 1.5 澄清门控(模糊形容词直接动手,做完才发现理解不一致)、Step 2 安全闸门(DB / 生产配置未确认即执行)、Step 4 验证证据 + Step 5 SELF-AUDIT(无证据交付)、长任务 handoff / Wave 的主模板锚点(上下文压缩后无法无损续做)。
+
+> 一句话:**渐进式路由保的是"工作流不断裂"**——可验证、可恢复、可审计的闭环都挂在它上面。你描述目标时感觉不到它,是因为它已经在后台把每道门按顺序挂好了。
+
+**那为什么 SKILL.md 显得很大?** 不是路由本身臃肿,而是同一份文件把"分发轨道 + 闭环步骤 + 护栏边界"三件必须共存的东西压在了一起:
+
+- **闭环步骤是硬骨架**:Step 0 记忆加载 → Step 1 意图路由 → Step 1.5 澄清门控 → Step 2 规划门禁 → Step 3 执行(7 防线)→ Step 4 验证证据 → Step 5 交付 + SELF-AUDIT → Step 6 复盘沉淀。每一步都不能省,否则闭环断一节。
+- **路由是轨道、护栏是闸**:路由表 / 领域路由表 / 优先级矩阵 / 意图三分法负责"分发",安全闸门、失败重试基线、对话流异常边界、子 Agent 边界、长任务可靠性负责"兜底"——它们必须和步骤写在同一份运行时指令里,模型加载 SKILL.md 时才一次拿到全部门控。
+- **为什么没拆进 references**:references/ 下 28 个文件是"各子技能的详细模板",而 SKILL.md 是"总控流程 + 跨步骤门控"。门控若拆出去,模型要先跳读多个文件才知道哪步该停,反而增加断裂风险。所以 SKILL.md 故意"大而全",references 才"专而深"。
+
+> 简言之:**大是为了不断裂**——闭环骨架 + 分发轨道 + 兜底护栏都必须在同一次加载里齐备,拆散反而会让模型在某步"忘了该不该停下"。真正会被按需懒加载的细节,都在 references/ 里,不用一次读完。
+
+## 子技能列表
+
+> **本节要点**:19 个子技能一表速览,说需求即自动匹配,不必背名字。
+
+| 子技能           | 功能                                                                                                                                                                               | 触发关键词                                                                                                                                                   |
+| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| 软件项目总控     | 通用软件项目从需求到交付的总控:边界、行为契约、架构、数据/API/集成、测试、安全、发布、回滚、监控、告警、巡检和沉淀                                                                | 软件项目, API服务, 后端服务, 后台模块, CLI工具, 数据脚本, 自动化任务, 插件项目, 完整功能, 项目交付, 部署, 发布, 回滚, 运维, 监控, 告警, 巡检                 |
+| 网站项目总控     | 从需求到上线的建站项目总控:站点规划、内容SEO、前端设计、CMS/API/数据、测试安全、性能部署、验收运维                                                                                | 做网站, 建站, 企业官网, 营销页, CMS网站, 网站上线, 网站交付                                                                                                  |
+| API设计          | 根据业务需求设计RESTful或GraphQL API接口,长任务采用 Init-Step-Poll 契约                                                                                                           | API设计, RESTful, GraphQL, 接口规范, AJAX防卡死, Init-Step-Poll, 长任务接口, 轮询接口                                                                        |
+| Bug诊断          | 分析错误日志和异常堆栈,定位Bug根因并给出修复方案                                                                                                                                  | Bug诊断, debug, 报错, 异常堆栈                                                                                                                               |
+| Karpathy编码规范 | 提供Karpathy核心编码哲学:思考优先、简洁至上                                                                                                                                       | Karpathy编码规范, Karpathy, 编码哲学                                                                                                                         |
+| Spec驱动开发     | 编码前对齐需求规格,使用OpenSpec artifact flow                                                                                                                                     | Spec驱动开发, spec, 需求对齐                                                                                                                                 |
+| 代码审查         | 审查代码质量,发现Bug、安全漏洞和代码缺陷                                                                                                                                          | 代码审查, code review, 代码缺陷                                                                                                                              |
+| 代码生成         | 根据功能需求生成高质量代码,含错误处理和边界条件                                                                                                                                   | 代码生成, 生成代码, 实现功能                                                                                                                                 |
+| 任务拆解与执行   | 将复杂需求拆分为原子任务,按Wave分组执行                                                                                                                                           | 任务拆解与执行, 任务分解, Wave执行                                                                                                                           |
+| 技术选型         | 根据项目需求推荐合适技术栈、框架和工具                                                                                                                                             | 技术选型, 技术栈, 框架选型                                                                                                                                   |
+| 文档生成         | 根据代码生成技术文档、README、API文档、部署说明和运维文档                                                                                                                          | 文档生成, API文档, README, 部署说明, 回滚说明, 运维文档                                                                                                      |
+| 测试用例生成     | 生成单元测试、集成测试、安全测试、性能测试和长任务测试                                                                                                                             | 测试用例生成, 单元测试, 测试用例, 安全测试, 性能测试, 长任务测试                                                                                             |
+| 重构建议         | 分析代码结构,识别坏味道并提供重构方案                                                                                                                                             | 重构建议, 重构, 坏味道, 代码异味                                                                                                                             |
+| 项目记忆管理     | 捕获上下文和决策,实现跨会话项目记忆沉淀                                                                                                                                           | 项目记忆管理, 项目记忆, 跨会话                                                                                                                               |
+| CMS二次开发      | PHP+MySQL CMS 二次开发全链路:CMS探测/PHP版本/数据库规范/安全红线/插件开发/长任务防卡死                                                                                            | CMS, 帝国CMS, WordPress, ThinkPHP, PHP8兼容, 二次开发, 插件开发, 批量任务, 导入导出, 生成静态页                                                              |
+| 前端设计         | UI/UX 与前端实现设计:设计思维、信息架构、视觉规范、品牌、Banner、图标、社媒图、响应式、可访问性、安全性、命名规范、目录规范、代码质量、ESLint基线、性能实现、浏览器验证、进度轮询 | 前端设计, UI设计, UX, 交互设计, 响应式, 设计系统, 前端安全, 命名规范, 目录规范, ESLint, 代码质量, 性能实现, 品牌设计, Banner, 图标, 社媒图, 进度条, 轮询状态 |
+| MySQL数据库      | MySQL 数据建模、SQL安全、索引设计、事务边界、慢查询诊断、迁移回滚和数据安全                                                                                                        | MySQL, 数据库设计, SQL, 索引, 事务, 慢查询, EXPLAIN, DDL, 迁移, 表结构, SQL优化                                                                              |
+| 性能基准测试     | 量化性能验证:识别触发面、测量耗时/内存/吞吐量、生成瓶颈报告、优化前后A/B对比                                                                                                      | 性能测试, benchmark, 基准测试, 耗时分析, 内存分析, 吞吐量, QPS, 瓶颈分析, 性能对比                                                                           |
+| 项目知识图谱     | 为项目自动构建代码结构依赖图谱(节点+依赖边),跨模块改动/重构/审查时查依赖闭包与影响面,agent 开发时借全局视角理解项目、定位更准改动更稳;纯 agent 受众,不生成 mermaid 可视化    | 依赖图, 模块关系, 谁依赖, 影响面, 代码结构图谱, 画依赖图, 依赖分析, 改这个会影响哪些文件                                                                     |
+
+> 你**不必背这 19 个技能**——说需求即自动匹配(见「使用方法」)。上表的作用是帮你快速了解「每个技能大概干什么、什么场景会自动触发」,方便你判断交付结果是否符合预期;想用得更好,扫一遍这张表即可,无需记忆触发词。
+
+## 使用方法
+
+> **本节要点**:对话自然触发、自动组合;「3 分钟上手」表 + 需求公式"目标+约束+验收"。
+
+通过 对话自然触发,说出需求即可自动匹配对应子技能。
+
+**你不用记这 19 个子技能,也不用手动编排组合。** 只要把目标说清楚(最好带约束和验收,见「需求表达完整示例」),系统会按领域路由表 + 优先级矩阵**自动串联**所需的子技能与专项参考——例如你说"实现导出功能并出接口文档",它会自动走 代码生成 + 文档生成;你说"定位这个报错顺带看看安全隐患",它会自动走 Bug诊断 + 代码审查。组合是**自动发生的**,你只需描述完整目标。
+
+Laravel、Eloquent、Blade、artisan、Migration、Form Request、Queue、PHPUnit、PHPStan 等关键词会触发 Laravel 专项参考;Java、Spring Boot、MyBatis、JPA、Maven、Gradle、JUnit、Mockito、JVM、GC、线程池、并发等关键词会触发 Java 专项参考;代码优化、性能优化、架构优化、N+1、缓存、异步、性能瓶颈等关键词会触发性能反模式审查与性能基准测试协同。专项参考与代码生成、测试用例生成、API设计、MySQL数据库、性能基准测试等现有子技能协同执行。
+
+### 3 分钟上手
+
+如果是第一次使用,不需要记住全部触发词,按下面入口说需求即可:
+
+| 你想做什么         | 推荐说法                                          | 会进入                               |
+| ------------------ | ------------------------------------------------- | ------------------------------------ |
+| 写一个功能         | "帮我实现登录接口,要求有参数校验和测试"          | 代码生成 + 测试用例生成              |
+| 修一个报错         | "这个报错帮我定位原因并给修复方案"                | Bug诊断                              |
+| 检查代码有没有问题 | "审查这个文件,重点看安全和性能"                  | 代码审查                             |
+| 做 CMS/PHP 二开    | "这是 WordPress/帝国CMS 项目,帮我加一个后台功能" | CMS二次开发                          |
+| 设计接口           | "设计一个订单导出接口,数据量大,需要进度条"      | API设计 + Init-Step-Poll             |
+| 换电脑继续上次任务 | "继续上次任务,先恢复 handoff"                    | 项目记忆管理                         |
+| 顺带做两件事       | "实现导出接口,顺带生成接口文档"                  | 代码生成 + 文档生成                  |
+| 不知道功能叫啥     | "帮我看看这段代码安不安全"                        | 代码审查(描述目标即可,不必记名字) |
+
+### 需求表达完整示例(初次使用照着抄,不用背技能名)
+
+光看表格还不够,下面给**真实可直接发送的句子**。记住一个万能公式:**目标 + 约束 + 验收**,越齐越省事(详见 FAQ 十六、Q"我不想被问太多")。
+
+**① 把"模糊需求"改成"可验收需求"(最推荐练手)**
+
+- ❌ 太模糊:`帮我把系统优化一下` → 会触发 Wayfinder 探索,先和你对齐再动手(不是错,但慢)
+- ✅ 直接命中:`用户列表接口响应超过 2s,帮我定位慢查询并加索引,目标降到 200ms 内` → 命中 `mysql-database` + `performance-benchmark`,且澄清少
+
+**② 带约束的功能开发(目标 + 范围 + 格式 + 权限)**
+
+- `给后台加一个导出用户的功能:范围=按筛选条件、格式=CSV、要鉴权+限频、能正确导出 1 万行且带表头` → 命中 `代码生成` + `doc-generation`,几乎不用追问
+
+**③ 一次要两个能力(顺带式)**
+
+- `实现登录接口,并生成单元测试` → `代码生成` + `测试用例生成`
+- `定位这个报错,顺带看看附近代码有没有安全隐患` → `Bug诊断` + `代码审查`
+
+**④ 不知道功能叫什么,只描述目标**
+
+- `帮我看看这段代码有没有安全隐患` → 自动命中 `代码审查`(你无需知道 `@code-review` 这个名字)
+- `这段逻辑太绕了,理一理结构` → 自动命中 `重构建议`
+
+**⑤ 纠正 / 反馈(让下次更准)**
+
+- `刚才那个修复不对,第 12 行的判空应该用 `if x is None`而不是`if not x`` → 触发修正并重跑验证;若排查耗时久,会被记入踩坑错误册供下次复用(见 FAQ 十七)
+
+> 关键:**说目标比背名字更稳**。不确定叫啥就直接描述,系统按关键词 + 意图三分法路由;命中不了会问你一句,不会偷偷用错技能。
+
+### ⚠️ 上手前先扫一眼红线(边界速览,完整版见 FAQ「能力边界速览 / 二、执行禁区 / 六、边界外」)
+
+新手最容易"无意踩坑"的边界,先记一眼,免得做到一半才发现不能做:
+
+- **本技能不覆盖的领域**:用户调研、工时排期、容器深度编排、IDE 配置、缺陷跟踪流程、安全深度扫描(只做边界说明,不替代专门流程)。
+- **明确禁止的做法(执行禁区)**:直拼 SQL、Shell 写中文/UTF-8 文件、跨模块扩散修改、硬编码密钥、跳过验证直接交付、安全/数据类失败重试、凭猜测补业务规则、快速通道执行数据库写入、跨模块(≥2)/批量替换(>10处) 走快速通道。
+- **关键限制**:单次最多加载 3 个 reference;子 Agent 禁止写/改代码(只做检索收集);代码审查(`@code-review`)是**只读**的、不自动改写实现;Laravel / Java / JS 三个专项 reference 不支持 `@` 显式调用(用关键词触发即可);涉及库写入 / 权限 / 生产配置须先给方案与回滚路径,不进快速通道。
+
+> 记不住全表也没关系:**"写代码 / 查 Bug / 做文档 / 审质量"都能做;问调研排期、动生产配置前先确认、别让 Agent 替你盲写库"就是边界。**
+
+> ⚠️ **重点**:红线不是建议——直拼 SQL / 盲写库 / 跳过验证直接交付,踩了会被安全闸门拦截、交付判无效,不是"提醒一下"。
+
+### 文档阅读顺序
+
+| 场景                       | 先看                                                | 再看                                  |
+| -------------------------- | --------------------------------------------------- | ------------------------------------- | --- |
+| 只想知道怎么用             | `README.md` 的"3 分钟上手"和"子技能列表"            | `FAQ.md`                              |
+| 不知道该用哪个技能         | `README.md` 子技能列表                              | `SKILL.md` 路由表                     |
+| 遇到报错、卡住、看不懂提示 | `FAQ.md`                                            | 对应 `references/*.md` 的失败回退机制 |
+| 长任务执行/续做/可靠交付   | `FAQ.md` 第九节                                     | `SKILL.md` 长任务执行可靠性专节       |
+| 要改技能执行规则           | `SKILL.md`                                          | 对应 `references/*.md`                |
+| 要看某个技能细节           | `references/` 下对应文件                            | `FAQ.md` 的反模式清单                 |
+| 想先知道哪些不能做 / 红线  | `FAQ.md`「能力边界速览」/ 二、执行禁区 / 六、边界外 | `SKILL.md` 安全闸门 / 澄清策略        |     |
+
+### 组合子技能与进阶触发
+
+- **不知道某个功能叫什么名字?** 不必背子技能名。路由表同时匹配「子技能名」和「功能说明里的场景词」,直接描述目标/动作即可:说"帮我看看这段代码有没有问题"会命中代码审查,说"这段逻辑太绕了理一理结构"会命中重构建议,说"写个带参数校验的接口"会命中代码生成。实在不确定就描述需求,系统按关键词 + 意图三分法路由,命中不了会向你确认,不会乱猜。
+- **想一次用两个功能?** 多数情况**不用你操心组合**——只要把完整目标说清(如"实现导出功能并出接口文档"),系统按领域路由表 + 优先级矩阵**自动串联**所需子技能(代码生成 + 文档生成),你不必手动指定谁先谁后。两种可选表达仅当你想**精确控制**时再用:①「顺带式」显式点名("...顺带生成接口文档");②「显式点名式」用多个 `@`("`@code-review` 先审,`@refactoring` 再按问题重构")。自动组合与显式调用都只跳过路由匹配、不影响协同加载。
+- **组合的典型顺序**:审查类(代码审查 / Bug诊断)是只读的,先出报告、你确认后,再走重构 / 代码生成落地修改;性能类先跑基准拿基线、再重构,禁止无基线声称"显著提升"。多子技能同时命中按优先级矩阵组合路由(首选 + 必要协同),单次最多加载 3 个 reference,过多会提示你分阶段做。
+
+## Hooks 自动守卫(可选,增强护栏)
+
+> **本节要点**:15 个可选护栏钩子;两种启用方式(占位符/安装器);多 IDE 工具名并集匹配。
+
+本技能在 `hooks.json` 中提供一组**通用示例**守卫钩子(共 15 个:PreToolUse 3 + PostToolUse 11 + PreCompact 1),对全部子技能与 JS 专项共用,作为强制护栏兜底机械项(备份、lint、PHP8 兼容、安全/脱敏、UTF-8、调试残留、SELECT \*、压缩快照、规划拦截、受保护目录写前拦截)。
+
+### 三个 hooks 文件的关系(模板 / 增量草案 / 安装产物)
+
+目录里会出现三个带 `hooks` 前缀的 JSON,分工如下,别混为一谈:
+
+| 文件                       | 类别                                         | 由谁产生                | 作用                                                                                                                                                                                                                                                                                         |
+| -------------------------- | -------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `hooks.json`               | **主模板(源,手写维护)**                   | 你自己维护              | 15 个跨平台守卫钩子的完整定义;占位符 `{{PYTHON_BIN}}`/`{{HOOKS_DIR}}` 由运行时或安装器替换。**已含** `capture_learning.py` 的 PostToolUse 项,但**不含** `UserPromptSubmit` 段                                                                                                              |
+| `hooks.capture-draft.json` | **捕获 hook 增量草案(源,手写维护,可选)** | 你自己维护              | 同一个 `capture_learning.py` 的 PostToolUse 项 + **额外一条 `UserPromptSubmit`(带 `--mode correction` 直接捕获你的纠错句式,信号最准)**。因 `UserPromptSubmit` 并非所有 IDE 都支持(不支持的 IDE 会因未知事件名导致整份配置加载失败),故**单列 opt-in**,要用就单独并入,不用不影响主配置 |
+| `hooks.installed.json`     | **安装产物(自动生成,勿手改)**             | `install_hooks.py` 生成 | 把 `hooks.json`(或指定源)里的占位符替换成真实机器路径后的可加载配置,直接接入运行时即可                                                                                                                                                                                                    |
+
+一句话:**`.json` 是手写源、`capture-draft.json` 是可选附加源(多一路最准的纠错捕获)、`.installed.json` 是编译输出**。日常只动前两个源文件,第三个由安装器重跑覆盖。
+
+**启用步骤**(两种方式任选其一):
+
+**方式 A:运行时占位符替换**(适用于支持变量替换的 IDE)
+
+1. 将 `hooks.json` 接入你的 Agent 运行时(按各 IDE 的 hooks 配置入口加载)。
+2. 设置两个环境变量:
+   - `PYTHON_BIN`:Python 解释器路径(如 `/usr/bin/python3` 或 `F:\BtSoft\python\python_python\python3.exe`)
+   - `HOOKS_DIR`:hook 脚本目录,可指向技能自带 `dev-expert/hooks/`,或你复制到项目的 `.codebuddy/hooks/`
+3. `hooks.json` 命令串用 `{{PYTHON_BIN}} "{{HOOKS_DIR}}/xxx.py"` 占位符,加载时由运行时替换为真实路径——**不写死任何机器路径**,换机换项目直接改环境变量即可。
+
+**方式 B:安装器预处理**(适用于运行时不支持占位符替换的 IDE,推荐跨平台使用)
+
+1. 运行安装器,自动探测 Python 路径和 hooks 目录,生成已替换占位符的 `hooks.installed.json`:
+
+   ```bash
+   # 自动探测(推荐)
+   python hooks/install_hooks.py
+
+   # 或通过参数指定
+   python hooks/install_hooks.py --python-bin /usr/bin/python3 --hooks-dir /path/to/hooks
+
+   # 或通过环境变量指定
+   PYTHON_BIN=/usr/bin/python3 HOOKS_DIR=/path/to/hooks python hooks/install_hooks.py
+   ```
+
+2. 将生成的 `hooks.installed.json` 接入你的 Agent 运行时(路径已硬编码,无需运行时替换)。
+3. 换机换项目时重跑安装器即可;`--in-place` 可原地覆盖(自动备份为 `.bak`),但建议保留 `hooks.json` 模板不变,仅使用 `hooks.installed.json`。
+
+> `hooks.installed.json` 文件顶部的 `_comment` 内含**各智能体平台适配说明**:CodeBuddy CN(工具名 `write_to_file`/`replace_in_file`)、Trae/Cursor 类(工具名 `Write`/`Edit`)等,matcher 已同时覆盖;若平台工具名不同(如 `MultiEdit`),在 matcher 里用 `|` 追加即可,无需改脚本。换机/换平台后 command 中的 Python 与 hooks 目录路径需同步更新(用安装器重生成或直接改 command)。
+
+> hooks 是护栏不是验证替代:动态/业务正确性(真实运行、端到端)仍须 Agent 显式产出证据。hooks 拦截即视为该防线未过,禁止绕过;运行时无 hooks 集成时须回退正文手动执行。钩子明细与故障排查见 `FAQ.md` 十五、Hooks 自动守卫。
+
+### 适配平台(多 IDE 兼容)
+
+本技能对 hooks 的适配基于**工具名并集匹配(matcher)**,不是为每个 IDE 写专属配置。只要你的 Agent 运行时的工具名落在下表某个家族里,hooks 即开箱可用;工具名不同则在 matcher 用 `|` 追加一行即可,无需改脚本。
+
+#### 平台适配矩阵
+
+| 平台                    | 工具名家族                                     | hooks 开箱覆盖                          | 说明                                                                                                  |
+| ----------------------- | ---------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------- | --------------- |
+| **CodeBuddy CN**        | `write_to_file` / `replace_in_file`            | ✅ 已覆盖                               | 国内 IDE,代码/历史已确认                                                                             |
+| **Trae / Trae CN**      | `Write` / `Edit`                               | ✅ 已覆盖                               | 代码/历史已确认                                                                                       |
+| **Cursor**              | `Write` / `Edit`                               | ✅ 已覆盖                               | 代码/历史已确认                                                                                       |
+| **Cline**               | `write_to_file` / `replace_in_file`            | ✅ 已覆盖                               | 寄生 VS Code,搜索核实其工具名与 CodeBuddy 同族                                                       |
+| **Roo Code**            | `write_to_file` / `replace_in_file`            | ✅ 已覆盖                               | Cline 分支,同族                                                                                      |
+| **Qoder CN**            | `write_to_file` / `replace_in_file`            | ✅ 已覆盖                               | 国内 IDE,v1.8.2「同类产品适配」已登记                                                                |
+| **WorkBuddy**           | `write_to_file` / `replace_in_file`            | ✅ 已覆盖                               | 国内 IDE,v1.8.2「同类产品适配」已登记                                                                |
+| **Claude Code**         | `Write` / `Edit` / `Bash` / `UserPromptSubmit` | ✅ PostToolUse;`UserPromptSubmit` 可选 | 支持事件最全,搜索核实其 hooks 含 `PreToolUse`/`PostToolUse`/`UserPromptSubmit`/`Stop`/`Notification` |
+| **Codex(OpenAI)**     | `Bash` / 命令类                                | ✅ 已覆盖                               | 命令类家族                                                                                            |
+| **OpenClaw(龙虾类)**  | 命令类                                         | ✅ 已覆盖                               | `capture-draft.json` 明确点名可在 `UserPromptSubmit` 挂捕获                                           |
+| **Gemini CLI**          | `Bash` / 命令类                                | ✅ 已覆盖                               | 命令类家族,同 Claude/Codex 模式                                                                      |
+| **Windsurf(Codeium)** | `write_file` / `edit_file`                     | ⚠️ 需 `                                 | ` 追加                                                                                                | 工具名与主流不同,在 matcher 补 `write_file | edit_file` 即可 |
+| **Zed**                 | 视配置                                         | ⚠️ 需确认                               | 工具名随扩展形态变化,确认后追加                                                                      |
+| **Continue**            | VS Code 扩展                                   | ⚠️ 需确认工具名                         | 确认其工具名后追加到 matcher                                                                          |
+| **Aider**               | CLI 自有机制                                   | ❌ 非工具名匹配                         | 走自带 hook / 包装层,不在本 matcher 范围                                                             |
+
+> ✅ = `hooks.json` matcher 已含该工具名家族,接入即生效;⚠️ = 在 matcher 用 `|` 追加对应工具名即可;❌ = 走另一套机制,本 hooks 不适用。
+
+#### 怎么追加未列出的平台
+
+若你的平台不在上表(或工具名不同,如 `MultiEdit`、`save_file`),只需编辑 `hooks.json` 每个 matcher 字符串,用 `|` 并入新工具名,例如把 `write_to_file|replace_in_file|Write|Edit` 改为 `write_to_file|replace_in_file|Write|Edit|MultiEdit|save_file`。**额外名称不会匹配时无害**,可放心并集。改完重跑安装器生成 `hooks.installed.json` 即可。
+
+各平台适配细节也写在 `hooks.installed.json` 顶部的 `_comment` 里。本技能除 hooks 外对运行时不挑平台——**核心路由/19 子技能是纯自然语言触发的,不接 hooks 也能用**,hooks 只是可选增强护栏。
+
+> ⚠️ **长任务 / 捕获 hook 要"自动执行 + 自动测试"须开放运行时权限**:`capture_learning.py`(学习捕获)与长任务闭环要自主跑通「执行命令 → 跑测试(`php -l` / `phpunit` / `node --check`)→ 落记录」,依赖运行时授予 `Bash` / `execute_command` 等工具的**免确认执行权限**。系统默认对危险操作(如**删除文件**、写入受保护目录、生产配置)会要求用户确认——未开放则长任务每步弹确认被卡死,自我改进闭环也无法自主推进。两层开放:① 技能侧 `SKILL.md` 的 `allowed-tools` 已声明 `Bash` 等工具;② 运行侧(IDE/平台权限策略)须允许这些工具 auto-approve(或任务前显式授权删除/写入等)。未开放时长任务须退化为「每步请求确认」模式,不得假设可无人值守自动执行(详见 `SKILL.md` frontmatter 的 `allowed-tools` 声明 与 `FAQ.md` 第九节)。
+
+## 协同技能
+
+> **本节要点**:14 条协同路径由路由自动串联,你只需说清目标,无需手动拼装。
+
+### 子技能内部协同
+
+> 下面 14 条协同路径是系统**自动执行**的编排结果——你描述完整目标后由领域路由表 + 优先级矩阵自动串联,**无需你手动拼接**。列出仅方便你了解内部如何协作、以及核对交付是否完整。你不必背这 19 个技能,说需求即自动匹配与组合。
+
+19个子技能通过"关联Skill"章节相互引用,形成全生命周期闭环。常见协同路径:
+
+- 通用软件项目:软件项目总控 → Spec驱动开发 → 技术选型/API设计/MySQL数据库/CMS二次开发 → 任务拆解与执行 → 代码生成 → 代码审查 → 测试用例生成 → 文档生成/部署运维说明 → 项目记忆管理
+- 网站项目:网站项目总控 → Spec驱动开发 → 任务拆解与执行 → 前端设计/CMS二次开发/API设计/MySQL数据库 → 代码生成 → 代码审查 → 测试用例生成 → 文档生成 → 项目记忆管理
+- 需求阶段:Spec驱动开发 → 任务拆解与执行 → 前端设计/代码生成
+- 页面阶段:前端设计 → 代码生成 → 代码审查 → 测试用例生成 → 文档生成
+- 交付阶段:代码生成 → 代码审查 → 测试用例生成 → 文档生成/部署运维说明
+- 治理阶段:Bug诊断/重构建议 → 代码审查 → Karpathy编码规范 → 项目记忆管理
+- CMS二开:CMS二次开发 → (前端设计/代码生成/Bug诊断/代码审查/技术选型) → 项目记忆管理
+- MySQL专项:MySQL数据库 → 代码生成/代码审查/Bug诊断 → 测试用例生成 → 项目记忆管理
+- 性能验证:性能基准测试 → 代码审查/重构建议/MySQL数据库 → 项目记忆管理
+- 代码优化:代码审查(性能反模式) → 性能基准测试(L0/L1/L2) → MySQL数据库/重构建议 → 测试用例生成 → 项目记忆管理
+- Laravel功能:Laravel专项参考 → 代码生成/API设计/MySQL数据库 → 代码审查 → 测试用例生成 + Laravel测试参考 → 项目记忆管理
+- Java功能:Java专项参考 → 代码生成/API设计/MySQL数据库 → 代码审查 → 测试用例生成 → 性能基准测试/文档生成
+- AJAX防卡死:Init → Step → Poll,API设计 → 前端设计 → CMS二次开发 → 测试用例生成
+- 沉淀阶段:任何子技能 → 项目记忆管理(记录决策/规范/summary)
+
+### 跨技能包协同
+
+本技能包为独立套件,暂无可直接联动的其他职业技能包。如需在编程任务中集成外部数据或服务,可使用主 Agent 当前可用的外部检索工具获取。
+
+## 文件结构
+
+> **本节要点**:技能目录清单与每个文件职责;知识图谱脚本在 `scripts/build_graph.py`。
+
+- `SKILL.md` - 技能运行时指令
+- `README.md` - 本文件,用户入口文档
+- `FAQ.md` - 常见问题、执行禁区、验证失败和边界外请求答疑
+- `hooks/` - 钩子脚本目录(**高级功能,需按自身 IDE/工具适配**):15 个守卫钩子(备份/lint/PHP8/安全/脱敏/UTF-8/调试残留/SELECT \*/依赖扫描/SQL注入/CMS风险/学习捕获/压缩快照/规划拦截/受保护目录拦截)+ `install_hooks.py` 安装器 + `step_ref_check.py` 步骤号检查器 + `recurrence_promote.py` 提升评估
+- `scripts/build_graph.py` - 项目知识图谱构建/查询工具(**高级功能,需按自身环境适配 Python 路径等**;**纯正则,无 AST/tree-sitter**;LSP 仅探测可用性记录、不参与抽取;构建与查询均**零 LLM token**):`--root` 全量构建 / `--query <file|symbol,多入口逗号分隔合并子图> [--direction up|down|both] [--depth N]` 子图裁剪 / `--rebuild` / `--incremental` / `--selftest`;输出写盘 `{PROJECT_ROOT}/.ai-memory/knowledge-graph/{graph.json,meta.json,symbols.json,graph.md}`。token 消耗对比见文末「附录 A」
+- `references/` - 子技能详细模板(共19个子技能)+ 8 个专项 reference(不计入子技能)+ 3 个工作流保障 reference
+- `references/laravel-development.md` - Laravel 开发专项参考(不计入子技能)
+- `references/laravel-testing.md` - Laravel 测试专项参考(不计入子技能)
+- `references/java-development.md` - Java/Spring 开发专项参考(不计入子技能)
+- `references/javascript-development.md` - JavaScript/Node.js 开发专项参考(不计入子技能);含「CMS / PHP 内联 JS」:PHP 内联 JS 强制校验(引号配对 / window.open features 收尾 / 全仓 Node 校验)
+- `references/domain-driven-design.md` - 领域驱动设计专项参考(不计入子技能);复杂业务系统领域建模:统一语言/限界上下文/聚合根/领域事件/防腐层
+- `references/distributed-systems.md` - 分布式系统设计专项参考(不计入子技能);跨服务事务/CAP/Saga/TCC/最终一致/幂等/分布式锁
+- `references/delivery-assurance.md` - 交付保障:执行率自检 / 收尾报告 / 确认超时 / Graceful Abort
+- `references/error-ledger.md` - 踩坑错误册:ERR-XXX 索引 + 单条模板 + 触发-定位-读取 + 新坑即录 + 归档
+- `references/execution-safety.md` - 执行安全:规划门禁 / 审计修复分离 / 批量修改防线 / 写码前确认 / 不卡死计数器 / 清单化质量+安全
+- `references/style-alignment.md` - 风格对齐(风格嗅探协议):二开/插件生成前强制取样原项目已有文件、提取风格标记、产出「风格基线」逐行对齐,禁止凭想象套用样式
+- `references/architecture-decision.md` - 架构决策(通用最小决策模板):候选方案 + trade-off + 反选论证 + 接受的代价 + 撤销条件;命中复杂度门槛时强制走决策流程
+
+## 维护建议
+
+> **本节要点**:reference 体量监控(建议 ≤400 行)与步骤号一致性检查命令。
+
+### Reference 体量监控
+
+单个 reference 文件建议控制在 **400 行以内**;超过时考虑拆分(如「设计规范」与「实现规范」分离)或提取共性内容到独立 reference。当前体量分布(按行数降序):
+
+| 文件                         | 行数   | 评估                                       |
+| ---------------------------- | ------ | ------------------------------------------ |
+| project-memory-management.md | ~479   | 偏大,职责内聚可暂保留                     |
+| frontend-design.md           | ~435   | 偏大,后续可拆分「设计规范」与「实现规范」 |
+| cms-development.md           | ~406   | 偏大,CMS 场景复杂度本身高                 |
+| code-review.md               | ~309   | 合理                                       |
+| 其余 21 个                   | 84-281 | 健康                                       |
+
+### 跨文件步骤号一致性检查
+
+新增/修改 reference 章节号后,运行检查器验证 SKILL.md / README.md / FAQ.md 中的步骤号引用与目标文件实际章节一致:
+
+```bash
+python hooks/step_ref_check.py
+```
+
+退出码 0 = 全部匹配;1 = 发现不匹配(会列出具体位置和期望/实际步骤号)。建议在合并 PR 前或重大版本发布前运行。
+
+---
+
+## 附录 A:知识图谱 token 消耗对比(常规 LLM 理解 vs 图谱方式)
+
+> **本节要点**:知识图谱 vs 常规 LLM 理解的 token 消耗对比——构建/查询均近零 LLM token(省的是"源码正文进上下文")。
+
+> 估算量级,非精确计费;基准:332 文件 / ~5 万行帝国 CMS 项目,图含 2890 有效依赖边,`MAX_NODES=80`(节点硬上限)。
+
+| 维度            | 常规 LLM 理解(无图谱)                            | 知识图谱方式                                   |
+| --------------- | -------------------------------------------------- | ---------------------------------------------- |
+| 构建 / 索引成本 | 无(每次现读源码)                                 | **0 token**(纯正则本地抽取,不进 LLM 上下文) |
+| 单次依赖查询    | 6k–60k token(把 5–50 个源文件喂进上下文理解依赖) | **1k–3k token**(返回子图 JSON,≤80 节点)     |
+| 10 次任务累积   | 60k–600k token                                     | 10k–30k token                                  |
+| 跨会话复用      | 每次重读,不留存                                   | 图谱缓存复用,query 恒定 ~2k                   |
+| 上下文污染      | 大(源码占满上下文窗口)                           | 小(仅依赖拓扑,不含实现细节)                 |
+
+单次跨模块依赖查询的 token 消耗(ASCII 示意,取中值):
+
+```
+常规 LLM 理解(无图谱)
+  读 10–50 个源文件理解依赖    ████████████████████████   6k – 60k tok
+知识图谱方式
+  返回子图 JSON(≤80 节点)     ██                         ~1k – 3k tok
+
+→ 单次节省约 70%–95%;跨会话 / 多任务累积节省更显著(图谱一次构建、永久复用)
+```
+
+**为何能省**:图谱把"代码 → 依赖关系"的抽取放在 LLM 之外(纯正则静态抽取,LSP 仅探测可用性、不参与抽取),LLM 只需消费精简后的依赖拓扑 JSON,而非原始源码。构建阶段 token≈0、查询阶段不重读源码,故整体近零 LLM 成本。详见 `references/project-knowledge-graph.md`。

+ 545 - 0
.claude/skills/dev-expert/SKILL.md

@@ -0,0 +1,545 @@
+---
+name: dev-expert
+description: 编程专家.Skill P8级编程助手,覆盖:软件/网站项目总控、API设计、Bug诊断、代码生成、代码审查、重构、测试用例、性能基准、技术选型、文档生成、任务拆解、Spec驱动、Karpathy规范、CMS二次开发、前端设计、MySQL、项目知识图谱。支持 @标识 显式调用跳过路由。
+version: "1.12.0"
+author: "智慧半岛"
+license: MIT
+allowed-tools:
+  - Read
+  - Grep
+  - Glob
+  - Shell
+  - Bash
+  - Edit
+  - Write
+  - Task
+  - WebSearch
+---
+
+# 编程专家.Skill -- P8级编程专家
+
+接到用户请求后,按以下流程执行(子技能清单见下方「子技能索引」)。
+
+## 执行流程
+
+执行流程对齐「六步闭环工作流」(分析→方案→执行→验证→交付→复盘),严禁跳过验证与复盘。
+
+### Step 0: 工作记忆加载(新会话强制执行)
+
+每次新会话首次响应前,必须按照 `project-memory-management` 第五步"新会话记忆恢复"的三层加载策略恢复历史上下文。在此基础上,本技能启用**写后即记**协议。
+
+**写后即记协议**
+
+> 进入 Step 1 前的历史记忆恢复,**统一由 Step 1.1 按 `project-memory-management` 第五步「新会话记忆恢复」三层策略执行**。本步骤不再重复定义读取协议,避免恢复文件集冲突与重复触发。
+
+读取 `project_memory.md` 时,优先定位「Glossary 术语表」节并加载,确保本轮对话使用的术语定义与历史记录一致。术语表格式与维护规则见 `project-memory-management` 第三步点五。
+
+---
+
+**写后即记协议**
+
+在以下"实质性工作"完成后,**立即**向当日日志追加一条记录(不经用户确认、不等待会话结束):
+
+| 触发条件                       | 动作                 |
+| ------------------------------ | -------------------- |
+| Bug 修复完成                   | 追加 `daily.md` 记录 |
+| 功能实现 / 代码生成完成        | 追加 `daily.md` 记录 |
+| 代码审查完成 / 重构完成        | 追加 `daily.md` 记录 |
+| 技术选型决策定案               | 追加 `daily.md` 记录 |
+| 配置变更 / 数据库迁移完成      | 追加 `daily.md` 记录 |
+| 文档生成 / 规范沉淀完成        | 追加 `daily.md` 记录 |
+| 项目约定 / 用户偏好新发现      | 追加 `daily.md` 记录 |
+| 纯信息查询、只读检查、临时测试 | **不触发**           |
+
+**写入目标**:`{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/daily.md`(append-only,UTF-8 无 BOM)
+
+**写入格式**(每条一条记录):
+
+```markdown
+## [HH:mm] - [动作类型]: [一句话摘要]
+
+- **文件**: [修改的文件列表]
+- **决策**: [如有]
+- **验证**: [验证方式 + 结果]
+```
+
+---
+
+**运行位置**:此协议运行在六步闭环工作流内。每完成一个 Step,若触及上述触发条件,即时追加到 `daily.md`;Step 6 的完整 Session Summary 仍按正常流程写入 `session_memory_{id}.jsonl`,两者互补不重复——`daily.md` 逐 Step 记录操作细节,`session_memory` 记录完整会话摘要。
+
+### Step 1: 分析指令 — 意图识别与路由匹配
+
+#### 1.1 恢复历史记忆(新会话强制执行)
+
+新会话首次响应前,必须调用 `project-memory-management` 第五步"新会话记忆恢复",按三层加载策略恢复历史上下文:
+
+1. **探测根目录**:按 `project-memory-management` 的"路径根目录探测协议"动态获取 `{PROJECT_ROOT}`(项目根目录,优先级:环境变量 > Git 根目录 > IDE 工作区 > 询问用户)和 `{USER_PROFILE}`(用户主目录),**严禁硬编码路径**
+2. **Layer 1 Metadata(~50 tokens,始终加载)**:读取 `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/topics.md`,获取项目名称、当前阶段、活跃任务、上次会话日期
+3. **Layer 2 Body(~200 tokens,连续会话时加载)**:读取最近一次 `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/session_memory_{session-id}.jsonl`,获取上次 Session Summary 和活跃决策
+4. **Layer 3 References(~500 tokens,按需加载)**:当用户说"继续/下一步"或涉及历史决策时,读取 `{PROJECT_ROOT}/.ai-memory/project_memory.md` 中相关决策记录和规范
+
+**触发规则**:
+
+- 用户说"继续/下一步/接着做"时,必须先加载 Layer 1+2,确认上次会话的主线目标、当前执行位置、阻塞状态
+- 加载失败或无历史记忆时,正常进入意图识别,不阻塞流程
+- 总 token 预算控制在 1000 以内,超过时优先丢弃最旧记录
+
+#### 1.2 意图识别与路由匹配
+
+分析用户输入,与下方路由表逐一比对。
+
+**显式调用优先**:用户输入若以 `@<英文标识>` 开头(如 `@code-generation 帮我实现JWT`),跳过关键词匹配,直接加载该子技能。标识必须严格等于下方「子技能索引」表「英文标识」列列出的合法标识之一,拼写错误自动回退关键词路由。位置约束:`@<标识>` 必须在输入开头或独占一段,邮箱、@提及等不触发。显式调用只跳过路由匹配,意图三分法、安全闸门、澄清策略、验证闭环、复盘沉淀全部照常执行。
+
+**任务-技能不匹配提示**:显式指定后,若任务实际特征与指定子技能存在强冲突,输出提示让用户确认,不擅自切换:
+
+| 用户指定           | 任务强信号                      | 提示动作                                              |
+| ------------------ | ------------------------------- | ----------------------------------------------------- |
+| `@code-generation` | 含"审查/review/检查问题/找漏洞" | 询问"任务更像代码审查,是否切换到 @code-review?"     |
+| `@code-review`     | 含"实现/写代码/生成/补接口"     | 询问"任务更像代码生成,是否切换到 @code-generation?" |
+| `@code-generation` | 含"报错/异常/堆栈/崩溃"         | 询问"任务更像 Bug 诊断,是否切换到 @bug-diagnosis?"  |
+| `@refactoring`     | 含"新功能/实现/新增"            | 询问"任务更像代码生成,是否切换到 @code-generation?" |
+| `@bug-diagnosis`   | 含"重构/优化/重写"              | 询问"任务更像重构建议,是否切换到 @refactoring?"     |
+
+用户确认前不执行;用户回复"按原指定执行"时立即按显式指定走,不再追问。
+
+**关键词匹配规则**(无显式调用时适用):
+
+- 用户输入中包含路由表中「子技能」列的关键词 → 匹配该子技能
+- 用户输入中包含路由表中「功能说明」列中提到的场景 → 匹配该子技能
+- 多个子技能同时匹配时,先按「子技能优先级矩阵」组合路由;无法组合时再选择匹配度最高的
+- 无法唯一确定时,向用户确认意图
+
+#### 意图三分法(决定执行策略)
+
+匹配子技能前,先判断请求类型,决定对话策略与自主度:
+
+| 类型     | 判定标准                                                                 | 动作                                           |
+| -------- | ------------------------------------------------------------------------ | ---------------------------------------------- |
+| 信息查询 | 用户只问概念、比较、解释、建议或只读评估,未要求改文件                   | 直接回答或只读检查,不修改文件                 |
+| 简单任务 | 目标明确、范围小、风险低、可用最小验证闭环                               | 进入快速通道,直接执行并交付验证证据           |
+| 复杂任务 | 涉及多文件、跨模块、数据库/配置/构建链、安全权限、批量治理或业务规则不明 | 先输出方案、影响范围和验证路径,必要时等待确认 |
+
+意图分类只决定执行策略,不替代子技能路由;分类后仍需按路由表加载对应 reference 模板。
+
+### Step 1.5: 需求澄清门控(复杂任务强制执行)
+
+意图三分法判定为「复杂任务」或路由匹配后存在以下信号时,强制进入本步骤,不可跳过:
+
+**触发信号**(满足任一即进入):
+
+- 用户需求含模糊形容词("快/稳定/安全/好用/优化一下")且未给出验收标准
+- 跨模块改动 ≥ 2 且模块边界未定义
+- 涉及数据模型变更但实体关系未澄清
+- 需求隐含多个利益方视角,未区分优先级
+- 用户描述少于 20 字且无上下文可推断
+
+**门控动作**:
+
+0. **先提案再纠(可行动需求默认动作)**:需求虽模糊但可行动时,先输出带权衡的推荐方案——候选方案 + 各自 trade-off + 推荐项 + 明确假设标注("如不符合请纠正"),再让用户纠正或确认;避免纯追问空转。仅当需求存在业务后果或不可逆风险时(见 `execution-safety.md` 澄清策略分级「高级」),才先问后提案。
+1. **边界追问**:对模糊需求逐项追问,将形容词转为可验收标准:
+   - "快" → 响应时间 < ?ms / 首屏 < ?s / 吞吐量 > ?QPS
+   - "稳定" → 可用性 ?% / 错误率 < ?% / 故障恢复 < ?min
+   - "安全" → 需覆盖哪些威胁(XSS/CSRF/SQL注入/权限绕过/...)
+   - "好用" → 关键路径操作步数 / 触控热区 / 加载感知时间
+
+2. **验收口径对齐**:输出 2-3 条可量化验收条件,由用户确认或修正。
+
+3. **假设显式化**:将当前推断的默认假设列出,标注"如不符合请纠正"。
+
+4. **范围确认**:明确本轮 In scope / Out of scope,防止需求漂移。
+
+**退出条件**:用户确认验收口径或明确授权"按默认假设继续"后方可进入 Step 2。
+
+**豁免条件**:意图三分法为「信息查询」或「简单任务」时跳过本步骤;显式调用 @spec-driven-development 时本步骤作为 Spec 驱动开发的前置输入,不重复执行。
+
+#### 模糊大型任务探索路径(Wayfinder 模式)
+
+当任务同时满足「复杂」且「目标模糊」(用户只说方向未说具体交付物)时,在需求澄清前先启动探索路径:
+
+1. **现状快照**:快速扫描相关模块,输出当前状态一句话摘要(模块数、文件数、关键依赖、已知痛点)
+2. **问题树展开**:按「症状 → 可能原因 → 验证方式」逐层展开 2-3 层,形成可探索的问题树
+3. **候选路径排序**:对问题树叶子节点按影响面 / 实现成本 / 风险排序,标注推荐路径
+4. **最小探索动作**:对排名第一的候选路径执行一个只读验证动作,根据结果收敛或调整方向
+5. **收敛为明确任务**:将验证后的路径转为 1-2 句可验收的任务描述,回到 Step 1.5 的验收口径对齐
+
+此路径是 Step 1.5 的**前置探索态**,二者不互斥:Wayfinder 仅负责把模糊方向收敛为明确任务,收敛后回到 Step 1.5 做验收口径对齐。仅在用户需求描述不足 30 字、且无历史上下文可推断时启用 Wayfinder;已有明确目标的复杂任务直接走 Step 1.5 门控。
+
+### Step 2: 制定方案 — 加载模板与前置检查
+
+1. **加载子技能模板**:根据子技能索引表找到对应文件路径,**必须**使用 `Read` 工具读取 `references/` 目录下的完整执行模板。
+2. **前置检查**(在执行模板步骤前必须完成):
+   - 按意图三分法确认执行模式:信息查询只读、简单任务快速通道、复杂任务先方案后执行
+   - 涉及生产配置、数据库写入、权限、凭证、隐私或不可回滚操作时,立即触发安全闸门,退出快速通道
+   - 业务规则、数据影响、权限边界、验收口径不明时,按澄清策略分级(高/中/低,定义见 [./references/execution-safety.md]「澄清策略分级」)决定是否补问
+3. **规划门禁(PLAN-GATE)**:复杂任务进入修改阶段前,必须按 [./references/execution-safety.md]「规划门禁」逐项打勾。Trivial Fix(单文件 ≤ 1 行,无结构变更)可跳过;若通过后触发异常则强制中止。未通过任一项 → 不退到修改阶段,先补齐缺口。
+4. **复杂任务依赖分析(P1,项目知识图谱)**:意图三分法 = 复杂任务(≥3 文件 / 跨模块)进入方案阶段时,先查图谱依赖闭包——`python scripts/build_graph.py --root {PROJECT_ROOT} --query <改动文件1,改动文件2,…> --direction both --depth 3`(查前自动走 第四步 新鲜度校验,过期则重建),**非项目全量**,将依赖矩阵喂入 `_plan.md`,替代临时 grep。简单任务 / 单文件(L0)跳过此步(图谱是噪声)。查后须按图谱子技能「查后动作规范」+ 硬门禁 G1/G2'/G3 执行——图谱仅作加速器,动刀前 grep 复核那一下不能省。**性能提示**:同一 Wave 内首次查询走新鲜度检测;同 Wave 后续步骤若源码未变,加 `--no-rebuild` 直接复用缓存图谱,避免每步全仓哈希重算(大仓显著提速)。仅在 Wave 首步或拓扑变更时省略 `--no-rebuild`。
+
+5. **确认输入与验收口径**:模板中标注「必填」的输入项缺失时,向用户索取;明确本轮验收标准与证据要求。
+
+### Step 3: 执行任务 — 按模板执行
+
+执行前必读 [./references/execution-safety.md],触发规则如下:
+
+- **执行前局部影响面(P2,项目知识图谱)**:Step 3 要改具体模块前,先查图谱该模块 2 跳上游——`python scripts/build_graph.py --root {PROJECT_ROOT} --query <模块> --direction up --depth 2`(查前自动走 第四步 新鲜度校验),将上游依赖方纳入改动影响评估范围,受影响文件在修改阶段一并改、验证阶段一并跑 lint/断言。图谱结论必须独立 grep 复核(见 `project-knowledge-graph` 硬门禁 G1 grep 冲突以 grep 为准 / G2' 须附 grep 证据 / G3 不可逆操作不单凭图谱),**图谱仅作加速器,不替代 grep 复核**。
+- **写码前实码确认**:修改/生成代码前,`read_file` 目标文件 + `search_content` 搜关联引用 + 冲突检查,三项均完成后才进入修改。跳过任一项 → 退回 Step 2。
+- **审计修复分离**:代码审查、Bug 诊断、重构建议场景,审计阶段只读不改码。审计结论输出(问题定位/根因/影响范围/修复方向/验证方式)确认后才进入修复。
+- **不卡死计数器**:同一诊断点连续失败 ≥ 3 次 → 暂停,列 2-3 条替代方案(标注**推荐项**并标记**已验证 / 未验证**)交用户选;5 分钟无回复 → 仅当推荐项**已验证**时按推荐项自动推进,不纯卡死(保留 SELF-AUDIT 兜底复检);若推荐项**未验证**或各方案均存疑 → 仍交用户决策,不自动推进带病方案。
+- **批量修改 7 防线**:同操作 ≥ 3 文件或 ≥ 10 处修改点时启用(预检→试点→备份→执行→后检→MD5→回读)。
+- **清单化质量自审 / 安全红线**:代码落笔前对照清单自审(收口于 Step 4 SELF-AUDIT)。
+
+严格按加载的模板逐步执行,每轮改动后立即运行对应验证。遵守 Karpathy 规范、非 Git 安全协议、上下文延迟加载协议。
+
+**TDD 与审查链前置判定**:复杂代码生成任务(涉及 ≥2 模块联动、数据持久化、网络通信或安全敏感,或代码预计 >200 行),进入 `code-generation` 模板后按其第〇步自动判定是否联动 `test-generation` 和 `code-review`,判定结果写入执行计划,不得跳过。
+
+### Step 4: 验证结果 — 证据采集与自检
+
+**验证证据是交付的硬性前提,无证据 = 未完成。**
+
+对照模板质量标准逐条验证,并执行以下自检:
+
+1. **清单化验证**:按 [./references/execution-safety.md]「清单化质量自审」10 条 +「安全红线清单」10 条逐项打勾,任一 ❌ 回退 Step 3 修复。
+2. **内联 JS 校验**:PHP 内联 JS 必须通过三道强制校验(引号配对 / `window.open` features 非空收尾 / 批量全仓 Node 校验),详见 [./references/javascript-development.md]「CMS / PHP 内联 JS」。
+3. **采集验证证据**(以下之一):
+
+| 证据类型  | 适用场景                        | 最小字段                               |
+| --------- | ------------------------------- | -------------------------------------- |
+| 命令+输出 | 代码 lint、测试运行、构建       | 命令文本 + 退出码 + 关键输出片段       |
+| 测试报告  | 单元/集成/回归测试              | 测试用例数、通过数、失败用例清单       |
+| 截图+步骤 | UI 交互、浏览器验证、视觉还原   | 截图 + 操作步骤 + 环境版本             |
+| API 响应  | 接口联调、长任务 Init/Step/Poll | Status Code + Response Body + 请求参数 |
+
+**子技能验证环节强制引用**:各子技能验证步骤必须明确声明本轮采用上述哪类证据并附最小字段(涉及 software-project/cms-development/code-generation/bug-diagnosis/website-project/frontend-design 等),否则视为未完成。
+
+未通过项返回 Step 3 修复;连续失败按「失败重试基线」处理(见轮次控制章节)。
+
+### Step 5: 交付结果 — 按模板格式输出
+
+按模板规定的格式输出结果。如果模板要求生成文件,写入后声明产出物。交付必须包含:
+
+- **执行率自检(SELF-AUDIT)**:按 [./references/delivery-assurance.md]「执行率自检」16 条逐项打勾,任一项 ❌ 回退对应阶段修复 → 重走 Step 4 → 复检。最多 3 轮,超出 → 列未修复项 + 原因 + 选项。
+- **步骤号一致性校验**:若本轮改动了 reference 的「第N步」章节号或跨文件步骤引用,运行 `python hooks/step_ref_check.py` 校验 SKILL.md/README.md/FAQ.md 中步骤号引用与目标 reference 实际章节号一致。
+- **变更摘要**(改了哪些文件、为什么改)
+- **验证证据**(Step 4 采集的结果)
+- **已知限制与剩余风险**
+- **收尾报告**:收尾模板见 [./references/delivery-assurance.md]「收尾报告模板」,必输出,禁止自由格式。
+
+交付前确认:[./references/delivery-assurance.md]「确认超时默认规则」适用本场景的默认行为。
+
+用户主动中止或遇到不可恢复阻断时,执行 [./references/delivery-assurance.md]「Graceful Abort」流程。
+
+### Step 6: 复盘沉淀 — 项目记忆更新
+
+**每次交付后强制进入此步骤,不得跳过。**
+
+调用 `project-memory-management` 沉淀本轮经验:
+
+1. **Session Summary**:已完成项、关键决策、遗留问题、下一步计划
+2. **Decision Record**:关键技术决策及撤销条件
+3. **Convention Capture**:新规范/编码约定/陷阱防范,提炼为可复用检查清单
+4. **已知问题清单**:新 Bug 模式、兼容性陷阱、第三方依赖风险
+5. **记忆维护检查**:若当日日志 `daily.md` 超过 8000 字符,触发精简提醒;若存在超过 30 天的日志目录,触发蒸馏提示(详见 `project-memory-management.md` 记忆维护协议)
+6. **术语漂移记录**:本轮出现的新术语定义或已有术语含义变更,追加到 `project_memory.md` 的 Glossary 节,格式为「术语名 | 规范名称 | 定义 | 记录日期」。详见 `project-memory-management` 第三步点五。
+
+7. **判断回溯录更新**(原「踩坑错误册」,见 [./references/error-ledger.md]):凡本轮新踩且排查耗时 ≥ 40 分钟的坑,按该册强制记录为**决策失败样本**——不仅记根因/修复/防范,还须填「错判点 / 正确判据 / 盲区」三栏(分配 ERR-ID + 写单条 + 更新索引 + 同步 project_memory.md Known Issues 缩写引用)。Bug 诊断/审查/重构 session 启动时先查索引,按错判形状做模式匹配(而非只查关键词),命中则读对应 ERR-ID 全文复用正确判据,并对本结论先跑证伪门禁。
+
+## 非 Git 文件操作安全协议
+
+本技能不依赖 Git 作为默认安全边界,所有文件修改遵循:
+
+1. **编辑前先读文件**:修改已有文件前必须先读取,确认上下文、编码和插入位置。
+2. **优先局部补丁**:用最小范围局部 diff 修改,禁止无必要全文重写;发现未知改动不得覆盖、清空、回滚。
+3. **中文与编码安全**:涉及中文、PHP、CMS 配置时,避免可能改变编码的 shell 写入方式。
+4. **验证失败不交付**:测试、构建、lint 失败时先修复;连续失败时输出阻塞原因和下一步所需输入。
+
+## 上下文延迟加载协议
+
+默认只读取入口文件、当前目标文件和直接引用文件。跨模块资料、历史记忆、reference 库或外部目录必须由用户明确指定、代码引用、错误证据、验证失败或当前任务依赖触发,避免上下文污染。
+
+## 安全闸门
+
+- 涉及生产配置、数据库写入、权限、凭证、隐私或不可回滚操作时,退出快速通道,先给方案和验证路径。
+- 发现密钥、Token、账号、内部路径时,只说明风险和处理动作,不展开敏感值。
+- 低风险可验证的编码任务不得因泛化安全理由拒绝。
+
+## 路由表
+
+| 子技能           | 功能说明                                                                                                                                                                       |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| 软件项目总控     | 通用软件项目从需求到交付的总控:边界、行为契约、架构、数据/API/集成、测试、安全、CI/CD、发布、回滚和沉淀。                                                                     |
+| 网站项目总控     | 从需求到上线的建站项目总控:站点规划、内容SEO、前端设计、CMS/API/数据、测试安全、性能部署、CI/CD、验收运维。                                                                   |
+| API设计          | 根据业务需求设计RESTful或GraphQL API接口,含Init-Step-Poll长任务模式、版本策略、鉴权、限流、幂等、错误码、文档和联调规范。                                                     |
+| Bug诊断          | 分析错误日志、异常堆栈和代码,定位Bug根因并给出修复方案。                                                                                                                      |
+| Karpathy编码规范 | Karpathy编码哲学:先思考、简洁优先、避免浪费、手工胜于模板。                                                                                                                   |
+| Spec驱动开发     | 编码前对齐需求规格,用OpenSpec的artifact flow分离提案。                                                                                                                        |
+| 代码审查         | 审查代码质量,发现潜在Bug、安全漏洞、性能问题和代码异味,输出分级问题清单与修复建议。                                                                                          |
+| 代码生成         | 根据功能需求生成高质量代码实现,支持多种编程语言和框架,包含错误处理和边界条件。                                                                                               |
+| 任务拆解与执行   | 将复杂需求拆分为原子任务,按依赖分Wave执行,上下文隔离。                                                                                                                       |
+| 技术选型         | 根据项目需求、团队能力和约束条件,推荐合适的技术栈、框架和工具。                                                                                                               |
+| 文档生成         | 根据代码生成技术文档,包括函数文档、README、API文档和架构说明。                                                                                                                |
+| 测试用例生成     | 根据代码逻辑生成单元测试、集成测试和边界条件测试,覆盖正常路径和异常路径。                                                                                                     |
+| 性能基准测试     | 量化性能验证:profiler火焰图、benchmark基准测试、内存分析,输出热点报告与优化前后对比。                                                                                        |
+| 重构建议         | 分析代码结构,识别坏味道,提供具体的重构方案和步骤,提升代码可维护性。                                                                                                         |
+| 项目记忆管理     | 捕获会话上下文、技术决策和项目规范,实现跨会话项目记忆沉淀与恢复。                                                                                                             |
+| CMS二次开发      | PHP+MySQL CMS 二次开发全链路指引:CMS探测、PHP版本选型、数据库规范、PHP8兼容、安全红线、插件开发。                                                                             |
+| 前端设计         | UI/UX 与前端实现设计:设计思维、信息架构、视觉规范、品牌、Banner、图标、社媒图、响应式、可访问性、安全性、命名规范、目录规范、代码质量、ESLint、性能实现、浏览器验证。         |
+| MySQL数据库      | MySQL 数据建模、SQL安全、索引设计、事务边界、慢查询诊断、迁移回滚和数据安全。                                                                                                  |
+| 项目知识图谱     | 为项目自动构建代码结构依赖图谱(节点+依赖边),跨模块改动/重构/审查时查依赖闭包与影响面,供 agent 获取全局依赖视角、提升改动定位准确度;纯 agent 受众,不生成 mermaid 可视化。 |
+
+## 领域路由表
+
+领域路由用于在关键词命中后进一步确认首选 reference,避免只按单词匹配导致误路由。先判断任务领域,再选择主模板;需要跨领域时按协同模板顺序补充加载。
+
+| 领域              | 触发信号                                                                                                                     | 首选 reference                          | 协同 reference                                                                      | 路由边界                                                                                                                                                                                                                        |
+| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 代码实现          | 实现功能、补接口、写脚本、改逻辑、生成代码                                                                                   | `code-generation`                       | `karpathy-coding-guidelines`, `test-generation`, `architecture-decision`            | 如果需求未对齐或涉及完整项目,先转 `spec-driven-development` 或项目总控                                                                                                                                                         |
+| Bug 诊断          | 报错、异常、堆栈、日志、复现失败、运行时行为不符                                                                             | `bug-diagnosis`                         | `code-review`, `test-generation`                                                    | 未读错误和上下文前不得直接改代码                                                                                                                                                                                                |
+| 代码审查          | review、审查、缺陷、安全风险、性能问题、可维护性                                                                             | `code-review`                           | `karpathy-coding-guidelines`, `refactoring`                                         | 以发现问题为主,不默认重写实现                                                                                                                                                                                                  |
+| 重构治理          | 重构、坏味道、结构混乱、重复代码、可维护性提升                                                                               | `refactoring`                           | `test-generation`, `code-review`                                                    | 未建立验证路径前不得扩大重构范围                                                                                                                                                                                                |
+| 测试补强          | 单元测试、集成测试、回归测试、边界用例、覆盖率                                                                               | `test-generation`                       | `code-generation`, `bug-diagnosis`                                                  | 先确认被测行为和预期结果                                                                                                                                                                                                        |
+| 性能验证          | profiler、火焰图、benchmark、cProfile、耗时分析、内存分析                                                                    | `performance-benchmark`                 | `code-review`, `refactoring`, `mysql-database`                                      | 先明确性能指标和阈值,不得无基线声称"显著提升"                                                                                                                                                                                  |
+| 文档交付          | README、API 文档、部署说明、回滚说明、技术文档                                                                               | `doc-generation`                        | `software-project`, `api-design`                                                    | 文档不得替代实际验证证据                                                                                                                                                                                                        |
+| API / 长任务接口  | REST、GraphQL、接口契约、AJAX、Init-Step-Poll、轮询                                                                          | `api-design`                            | `frontend-design`, `cms-development`                                                | 长任务必须采用 Init-Step-Poll 架构                                                                                                                                                                                              |
+| Laravel / PHP框架 | Laravel、Eloquent、Blade、artisan、Migration、Form Request、Queue、PHPUnit、PHPStan                                          | `laravel-development`                   | `code-generation`, `test-generation`, `laravel-testing`, `mysql-database`           | 仅在确认 Laravel 项目后加载;不得套用到未确认框架的 CMS 项目                                                                                                                                                                    |
+| Java / Spring     | Java、Spring Boot、Spring、MyBatis、Hibernate、JPA、Maven、Gradle、JUnit、Mockito、JVM、GC、线程池、并发                     | `java-development`                      | `code-generation`, `code-review`, `test-generation`, `performance-benchmark`        | 仅在确认 Java 项目后加载;Java 17/21 特性必须先确认运行版本支持                                                                                                                                                                 |
+| JavaScript        | JavaScript、JS、ES6、ES module、CommonJS、JS 风格规范、JS 代码检查、全角符号修复、PHP 内联 JS、window.open、onclick 事件属性 | `javascript-development`                | `code-generation`, `code-review`, `bug-diagnosis`, `api-design`, `software-project` | 仅在确认 JS 项目后加载;TypeScript 项目由其类型系统承担约束;Node.js 版本需 >= 15(推荐 18+);JS 写在 PHP 文件内联时须额外走「CMS / PHP 内联 JS」三道强制校验(引号配对 / window.open features 非空收尾 / 批量全仓 Node 校验) |
+| CMS / PHP         | CMS、EmpireCMS、WordPress、ThinkPHP、PHP8兼容、插件、模板                                                                    | `cms-development`                       | `mysql-database`, `bug-diagnosis`, `code-generation`                                | 未确认 CMS 类型前禁止生成框架特定代码                                                                                                                                                                                           |
+| MySQL / 数据库    | 表结构、SQL、索引、事务、慢查询、EXPLAIN、迁移、回滚                                                                         | `mysql-database`                        | `cms-development`, `software-project`                                               | 写操作必须先确认影响范围和回滚路径                                                                                                                                                                                              |
+| 前端 / 视觉       | UI、UX、页面、响应式、品牌、Banner、图标、浏览器验证                                                                         | `frontend-design`                       | `code-generation`, `website-project`                                                | 先输出设计约束,再进入实现                                                                                                                                                                                                      |
+| 项目总控          | 完整功能、项目交付、上线、发布、运维、监控、巡检                                                                             | `software-project` 或 `website-project` | `task-decomposition-and-execution`, `doc-generation`                                | 先定边界、验收和发布回滚,再拆任务                                                                                                                                                                                              |
+| 项目记忆          | 项目记忆、上下文恢复、决策记录、规范沉淀、继续上一轮                                                                         | `project-memory-management`             | 当前主任务 reference                                                                | 项目记忆只辅助主任务,不替代主任务交付                                                                                                                                                                                          |
+| 领域建模          | 领域驱动、领域建模、限界上下文、聚合根、领域事件、统一语言、防腐层、复杂业务规则建模、遗留系统隔离                           | `domain-driven-design`                  | `software-project`, `spec-driven-development`, `architecture-decision`              | 仅复杂业务系统(多子域/术语多义/跨上下文)加载;CRUD 脚本/营销页不加载,避免过度设计                                                                                                                                            |
+| 分布式系统        | 分布式事务、CAP、一致性、Saga、TCC、2PC、最终一致、幂等、分布式锁、消息驱动、事件驱动、跨服务事务                            | `distributed-systems`                   | `code-generation`, `architecture-decision`, `api-design`                            | 仅跨服务/跨库/消息驱动场景加载;单机单库 CRUD 不加载,避免过度设计                                                                                                                                                              |
+
+领域路由后仍须执行意图三分法:信息查询只读回答,简单任务可进快速通道,复杂任务先方案确认。
+
+### 专项 reference 映射
+
+以下专项 reference 只作为领域协同资料按需加载,不加入 `@英文标识` 显式调用索引,也不计入子技能总数。领域路由命中这些标识时,必须按下表读取真实文件路径,禁止依赖裸标识自行推断。
+
+| 专项 reference           | 文件                                     |
+| ------------------------ | ---------------------------------------- |
+| `laravel-development`    | [./references/laravel-development.md]    |
+| `laravel-testing`        | [./references/laravel-testing.md]        |
+| `java-development`       | [./references/java-development.md]       |
+| `javascript-development` | [./references/javascript-development.md] |
+| `execution-safety`       | [./references/execution-safety.md]       |
+| `delivery-assurance`     | [./references/delivery-assurance.md]     |
+| `error-ledger`           | [./references/error-ledger.md]           |
+| `style-alignment`        | [./references/style-alignment.md]        |
+| `architecture-decision`  | [./references/architecture-decision.md]  |
+| `domain-driven-design`   | [./references/domain-driven-design.md]   |
+| `distributed-systems`    | [./references/distributed-systems.md]    |
+
+## 子技能索引
+
+> 用户可在输入开头加 `@英文标识` 显式指定子技能,跳过关键词路由(详见 Step 1.2)。下表「英文标识」列即为合法标识符。
+
+| 子技能           | 英文标识                           | 文件                                               |
+| ---------------- | ---------------------------------- | -------------------------------------------------- |
+| 软件项目总控     | `software-project`                 | [./references/software-project.md]                 |
+| 网站项目总控     | `website-project`                  | [./references/website-project.md]                  |
+| API设计          | `api-design`                       | [./references/api-design.md]                       |
+| Bug诊断          | `bug-diagnosis`                    | [./references/bug-diagnosis.md]                    |
+| Karpathy编码规范 | `karpathy-coding-guidelines`       | [./references/karpathy-coding-guidelines.md]       |
+| Spec驱动开发     | `spec-driven-development`          | [./references/spec-driven-development.md]          |
+| 代码审查         | `code-review`                      | [./references/code-review.md]                      |
+| 代码生成         | `code-generation`                  | [./references/code-generation.md]                  |
+| 任务拆解与执行   | `task-decomposition-and-execution` | [./references/task-decomposition-and-execution.md] |
+| 技术选型         | `tech-selection`                   | [./references/tech-selection.md]                   |
+| 文档生成         | `doc-generation`                   | [./references/doc-generation.md]                   |
+| 测试用例生成     | `test-generation`                  | [./references/test-generation.md]                  |
+| 性能基准测试     | `performance-benchmark`            | [./references/performance-benchmark.md]            |
+| 重构建议         | `refactoring`                      | [./references/refactoring.md]                      |
+| 项目记忆管理     | `project-memory-management`        | [./references/project-memory-management.md]        |
+| CMS二次开发      | `cms-development`                  | [./references/cms-development.md]                  |
+| 前端设计         | `frontend-design`                  | [./references/frontend-design.md]                  |
+| MySQL数据库      | `mysql-database`                   | [./references/mysql-database.md]                   |
+| 项目知识图谱     | `project-knowledge-graph`          | [./references/project-knowledge-graph.md]          |
+
+## 子技能优先级矩阵
+
+当用户输入同时匹配多个子技能时,按以下优先级路由:
+
+| 场景                                                   | 优先子技能                       | 理由                                                                                                                                           |
+| ------------------------------------------------------ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| "做网站/建站/企业官网/营销页"                          | 网站项目总控                     | 网站项目需要先覆盖项目启动、站点规划、SEO、部署、验收和运维,再拆解执行                                                                        |
+| "API服务/后端服务/CLI工具/数据脚本/插件项目/完整功能"  | 软件项目总控                     | 非网站类完整项目需要先覆盖边界、架构、验证、发布和交付,再拆解执行                                                                             |
+| "帮我看看这段代码有什么问题"                           | 代码审查                         | 通用审查优先于专项重构                                                                                                                         |
+| "这段代码有坏味道/代码异味"                            | 重构建议                         | 专项关键词触发专项技能                                                                                                                         |
+| "帮我修复这个Bug"                                      | Bug诊断                          | 明确修复意图优先于审查                                                                                                                         |
+| "帮我写段代码" + 提到测试                              | 代码生成                         | 先生成主代码,再生成测试(代码生成→测试用例生成 协同)                                                                                         |
+| "设计API" + 提到技术选型                               | 技术选型                         | 选型先于设计(技术选型→API设计 协同)                                                                                                          |
+| "重构" + 提到测试                                      | 重构建议                         | 先重构,再补测试(重构建议→测试用例生成 协同)                                                                                                 |
+| "写文档" + 提到API                                     | 文档生成                         | 通用文档优先,API专项由 API设计 协同                                                                                                           |
+| 任何子技能 + "记录决策"                                | 当前子技能 + 项目记忆管理        | 主任务优先,记忆作为附属步骤                                                                                                                   |
+| "CMS二次开发" + "写代码"                               | CMS二次开发 + 代码生成           | CMS规范优先,代码生成遵循CMS数据访问层和安全红线                                                                                               |
+| "帝国CMS/WordPress" + "报错"                           | Bug诊断                          | CMS关键词触发Bug诊断时自动加载CMS常见Bug模式                                                                                                   |
+| "PHP" + "代码审查"                                     | 代码审查 + CMS二次开发           | 审查PHP代码时自动追加CMS安全审查清单                                                                                                           |
+| "Laravel/Eloquent/Blade/artisan" + "写代码/改功能"     | 代码生成 + Laravel专项参考       | Laravel 框架约定优先,按需加载 `laravel-development`                                                                                           |
+| "Laravel/PHPUnit/Pest/Feature Test" + "测试"           | 测试用例生成 + Laravel测试参考   | Laravel 测试优先使用 Feature Test、Factory、Facade fake 和数据库断言                                                                           |
+| "Java/Spring Boot/MyBatis/JPA" + "写代码/改功能"       | 代码生成 + Java专项参考          | Java/Spring 分层、事务、数据访问和异常处理规则优先,按需加载 `java-development`                                                                |
+| "Java/JVM/GC/线程池/并发" + "性能/调优"                | 性能基准测试 + Java专项参考      | JVM 与并发问题必须先采集耗时、GC、线程、堆或连接池证据                                                                                         |
+| "Java/JUnit/Mockito/Spring Boot Test" + "测试"         | 测试用例生成 + Java专项参考      | Java 测试优先区分单元测试、切片测试、集成测试和外部依赖替身                                                                                    |
+| "JavaScript/ES6" + "写代码/改功能"                     | 代码生成 + JS专项参考            | JS 架构规则、模块系统、JS语法检查优先,按需加载 `javascript-development`                                                                       |
+| "JavaScript" + "审查/检查/review"                      | 代码审查 + JS专项参考            | 审查 JS 代码时自动加载 JS 代码风格规范和代码质量检查流程                                                                                       |
+| "JS代码检查/全角符号/语法检查/兼容检查"                | JS专项参考                       | 加载代码质量检查 4 步流程(Node.js 版本检查→语法检查→修复→验证)                                                                               |
+| "MySQL/数据库/SQL/索引/慢查询/EXPLAIN"                 | MySQL数据库                      | 数据结构、SQL安全和性能问题优先走数据库专项模板                                                                                                |
+| "代码优化/性能优化/架构优化/N+1/缓存/异步/性能瓶颈"    | 性能基准测试 + 代码审查          | 先按性能反模式静态扫描定位嫌疑点,再用 benchmark/profile/EXPLAIN 验证                                                                          |
+| "PHP/CMS" + "数据库/SQL"                               | CMS二次开发 + MySQL数据库        | 先确认CMS访问层和表前缀,再进行SQL/索引/迁移设计                                                                                               |
+| "部署/发布/上线/回滚/运维/监控/告警/巡检"              | 软件项目总控 + 文档生成          | 发布运维类请求必须输出发布步骤、回滚方案、观测指标、告警和巡检清单                                                                             |
+| "前端/页面/UI" + "设计"                                | 前端设计                         | 视觉、交互、响应式和可访问性优先于直接写代码                                                                                                   |
+| "品牌/Banner/图标/社媒图"                              | 前端设计                         | 视觉资产类请求由前端设计输出规格、风格、尺寸和验收标准                                                                                         |
+| "前端设计" + "写代码"                                  | 前端设计 + 代码生成              | 先定义页面结构/组件状态/响应式,再生成实现代码                                                                                                 |
+| "CMS模板" + "页面设计"                                 | 前端设计 + CMS二次开发           | 同时约束视觉实现、模板变量、输出转义和缓存策略                                                                                                 |
+| "前端安全/XSS/CSRF/CSP"                                | 前端设计                         | 前端产物必须通过安全红线检查:输出转义、Token、敏感信息不入前端、接口权限后端兜底                                                              |
+| "spec/需求对齐/需求规格"                               | Spec驱动开发                     | 编码前必须先对齐需求规格,生成 Scenario 和验收标准                                                                                             |
+| "任务分解/Wave执行"                                    | 任务拆解与执行                   | 复杂需求应拆分为原子任务按Wave分组,避免单次执行超限                                                                                           |
+| "Karpathy/编码哲学/简洁优先"                           | Karpathy编码规范                 | 编码哲学优先于具体实现——思考先于编码、简洁先于完备                                                                                             |
+| "profiler/火焰图/benchmark/cProfile/耗时分析/内存分析" | 性能基准测试                     | 定量性能验证优先于代码审查的静态推断                                                                                                           |
+| "review" + "性能"                                      | 代码审查 + 性能基准测试          | 先静态审查发现嫌疑点,再用 benchmark 定量验证                                                                                                  |
+| "重构" + "基准/对比"                                   | 重构建议 + 性能基准测试          | 重构前跑基线,重构后跑对比,量化收益                                                                                                           |
+| "AJAX防卡死/Init-Step-Poll/长任务/轮询"                | API设计 + 前端设计 + CMS二次开发 | 长任务必须先定义 Init/Step/Poll 接口契约,再实现前端轮询和 CMS 分批处理                                                                        |
+| "跨模块改动/重构/审查/接手陌生项目" + "依赖/影响面"    | 项目知识图谱 + 当前子技能        | 复杂任务(≥3 文件)先查图谱依赖闭包(P1 Step2 规划前置),改码前查上游影响面(P2 Step3),图谱仅作加速器、动刀前 grep 复核(G1/G2'/G3 硬门禁) |
+
+**互斥规则**:
+
+- 代码审查 vs 重构建议:用户说"问题/缺陷/漏洞" → 代码审查;用户说"坏味道/异味/重构" → 重构建议
+- Bug诊断 vs 代码审查:用户说"报错/Bug/崩溃" → Bug诊断;用户说"审查/检查/review" → 代码审查
+- 代码生成 vs 重构建议:用户说"实现/写/生成" → 代码生成;用户说"重构/优化/改" → 重构建议
+
+**协同顺序规则**:
+
+- 网站项目:网站项目总控 → Spec驱动开发 → 任务拆解与执行 → 前端设计/CMS二次开发/API设计 → 代码生成 → 代码审查 → 测试用例生成 → 文档生成 → 项目记忆管理
+- 通用软件项目:软件项目总控 → Spec驱动开发 → 技术选型/API设计/MySQL数据库/CMS二次开发 → 任务拆解与执行 → 代码生成 → 代码审查 → 测试用例生成 → 文档生成/部署运维说明 → 项目记忆管理
+- 需求→实现:Spec驱动开发 → 任务拆解与执行 → 代码生成 → 代码审查 → 性能基准测试 → 测试用例生成 → 文档生成
+- 页面→实现:前端设计 → 代码生成 → 代码审查 → 性能基准测试 → 测试用例生成 → 文档生成
+- CMS页面→实现:CMS二次开发 → 前端设计(含浏览器验证) → 代码生成 → 代码审查 → 测试用例生成
+- Laravel功能→实现:Laravel专项参考 → 代码生成/API设计/MySQL数据库 → 代码审查 → 测试用例生成 + Laravel测试参考 → 文档生成
+- Java功能→实现:Java专项参考 → 代码生成/API设计/MySQL数据库 → 代码审查 → 测试用例生成 → 性能基准测试/文档生成
+- JS功能→实现:JS专项参考 → 代码生成/API设计 → 代码审查 → 测试用例生成 → 文档生成
+- JS代码质量→验证:JS专项参考(代码质量检查流程)→ 代码审查 → 代码生成(修复)→ JS专项参考(验证)
+- 代码优化→验证:代码审查(性能反模式) → 性能基准测试(L0/L1/L2) → MySQL数据库/重构建议 → 测试用例生成 → 项目记忆管理
+- 治理→沉淀:Bug诊断/重构建议 → 代码审查 → Karpathy编码规范 → 项目记忆管理
+
+## 跨技能协同指引
+
+本技能为独立套件,不联动其他职业技能;接入外部数据/服务时复用主 Agent 现有检索工具即可。
+
+### 本技能边界声明
+
+以下工作不在本技能范围,出现相关请求时只做边界说明,不尝试覆盖:
+
+| 不在范围                           | 原因                                                     | 建议动作                                                         |
+| ---------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------- |
+| 用户调研/需求挖掘                  | 属产品/用户体验研究,非编码                              | 标注「需求工程上游」,建议用户先提供调研结论                     |
+| 工时估算/项目排期/里程碑规划       | 属项目管理                                               | 建议由项目经理或专门工具承接                                     |
+| 容器化编排                         | 本技能面向 PHP/CMS/前后端开发,未覆盖完整容器编排能力    | 支持构建/测试/部署流水线阶段划分与配置片段;K8s 深度编排不在范围 |
+| 开发环境搭建、IDE 配置、调试器安装 | 属工程平台                                               | 由用户自行配置或参考官方文档                                     |
+| 缺陷跟踪流程、Issue 管理工链       | 属项目管理工具链                                         | 建议接入 Jira、GitLab Issues 等专门工具                          |
+| 安全深度审查                       | 本技能自带代码审查可发现常见漏洞;深度扫描属独立安全工程 | 建议由独立安全审查流程承接,不在本技能内展开                     |
+
+遇到上述请求时,仍可读取代码或给出最小建议,但不得声称已完整覆盖该领域,也不得用泛化方案替代专门流程。
+
+## 执行边界与熔断
+
+### 子Agent 边界(L0)
+
+启用子 Agent 时以下边界不可绕过:**检索收集型**仅返回 `文件:行号 + 原文`,禁推理归纳;**执行型**仅在主 Agent 授权且已审查边界下落码;**审查型**仅输出问题清单。核心禁令:禁改码(写码须主 Agent 自写或经授权复核)、方案须主 Agent 复核后落地、子 Agent 不继承本技能铁律。委派前主 Agent 必须先输出目标/边界/验收口径;未输出即委派触发失败模式收回任务。子 Agent 不得执行数据库写入/生产配置/权限变更/凭证处理/绕过安全闸门。返回后主 Agent 须运行验证,未验证即转述视为未完成。
+
+### 轮次与收敛
+
+| 参数             | 默认值                         |
+| ---------------- | ------------------------------ |
+| 默认循环轮次     | 3                              |
+| 安全最大轮次     | 6                              |
+| 每轮最大改动点数 | 3                              |
+| 失败熔断         | 同一Bug 2轮未修复→标记已知限制 |
+| 低收益检测       | 连续2轮仅P2微调→建议提前结束   |
+
+收敛逻辑:终极功能完成且测试通过→正常结束;无终极功能达到默认轮次且测试通过→默认结束;达到安全最大轮次→强制结束当前 Wave,输出未完成清单(长任务转交下一 Wave / 写 `handoff.md` 续做,已验证 Wave 产出物不回退清零);连续2轮仅 P2 级→建议结束。
+
+### 失败重试基线
+
+| 失败类型        | 最大重试 | 说明               |
+| --------------- | -------- | ------------------ |
+| 安全/数据类     | 0        | 立即升级不重试     |
+| Lint/语法类     | 3        | —                  |
+| 验证类失败      | 2        | —                  |
+| 环境类失败      | 1        | 重试后升级         |
+| 业务规则不明    | 0        | 向用户索取输入     |
+| 网络/远程服务类 | 2        | 写操作前须确认幂等 |
+
+各 reference 失败回退表如与本基线冲突,以本基线为准。
+
+### 错误提示
+
+失败时必须输出:发生了什么(一句话)、可能原因(1-3 个,标注已确认/待验证)、修复方向(最小动作)、风险提醒(涉生产/数据库/凭证时请求确认)、需用户提供什么(仅在缺输入时提出,格式"需 [角色] 提供 [具体输入]")。禁止只输出"报错了/失败了"。
+
+### 网络重试
+
+临时超时/5xx 最多 2 次自动重试。写操作/扣费/发消息/4xx/权限失败/参数错误不自动重试。重试后仍失败时输出状态和下一步,不静默扩大执行范围。
+
+### 对话流异常边界
+
+| 失败模式     | 触发阈值                                                   | 回退动作                             |
+| ------------ | ---------------------------------------------------------- | ------------------------------------ |
+| 过度路由     | 加载 reference > 3 且任务为单文件修复或纯查询              | 回到意图三分法,只保留首选 reference |
+| 澄清不足     | 涉数据库/权限/生产配置且未确认即执行                       | 下调自主度,补问关键问题             |
+| 澄清过度     | 低风险任务连续追问 ≥ 3 次                                  | 进入快速通道,说明默认假设直接执行   |
+| 快速通道误判 | 快速执行中发现跨模块(≥2)/数据库写入/批量替换(>10)/不可回滚 | 立即退出快速通道,输出方案和验证路径 |
+| 理解委派     | 未输出目标/边界/验收口径即委派子任务                       | 收回任务回到需求分析                 |
+| 跳过复核     | 子任务返回后未验证即转述                                   | 对照验收口径复核                     |
+| 安全闸门失效 | 输出含 Token/密码/密钥                                     | 隔离不可信输入,只说明风险           |
+| 过度拒绝     | 合法编码且可验证却被安全理由阻断                           | 保留必要边界继续交付                 |
+
+每次触发须标注:触发原因、回退动作、当前执行模式和剩余风险。
+
+### 长任务执行可靠性(L0,不可绕过)
+
+长任务须保证中途可恢复、结束有可靠产出物(与产品侧 Init→Step→Poll 构成两层协议,见对应 reference)。
+
+- **轮次上限仅约束单 Wave**:「安全最大轮次 = 6」不跨 Wave 累计。大任务应 Wave 拆分 + 跨会话 `handoff.md` 续做;已验证 Wave 产出物不回退清零。
+- **周期性检查点**:每完成一个 Wave 或累计 5 个原子任务,落 `.ai-memory/handoff.md` 检查点:`已完成项(带验证证据)` → `相关文件` + `未完成项(下一步)` + `阻塞项`。目的:上下文压缩/崩溃后无损续做。
+- **两层进度可验证**:系统层 Init/Step/Poll/Cancel/失败重试/僵尸检测各有验证证据(见 `software-project.md` 第六步);Agent 层每个原子任务产出自检 Task Summary(状态/文件/证据/置信度/偏差/遗留),未经复核不得计入完成度。
+- **可靠交付物四要素(呼应 Step 5)**:① 已完成清单(每项带验证证据)② 未验证项/已知限制(不得隐藏)③ 部分失败明细(批次成败、可 resume、失败原因)④ 下一步(续做动作与输入)。
+- **上下文污染防护**:只加载当前 Wave/原子任务所需上下文,已完成 Wave 仅以 `previous_summary` 传递。
+
+### 验收标准
+
+| 检查维度   | 检查项                                 | 判定标准                                          |
+| ---------- | -------------------------------------- | ------------------------------------------------- |
+| 功能完整性 | 必选功能全部可运行                     | 全部必选功能可运行                                |
+| 代码质量   | 无阻塞级代码问题                       | 无阻塞级代码问题                                  |
+| 构建通过   | 编译/运行成功                          | 构建状态为成功                                    |
+| 测试覆盖   | 核心路径有测试                         | 核心路径有测试用例                                |
+| 规范符合性 | Karpathy + 项目规范                    | 无规范违规                                        |
+| 可追溯性   | 变更记录完整                           | 变更文件与影响范围可查                            |
+| 逻辑一致性 | 需求→代码映射完整                      | 需求→代码映射关系完整可追溯                       |
+| 长任务可靠 | 有检查点 + 未验证项/部分失败明细未隐藏 | 有 handoff/状态文件;交付含未验证项与部分失败明细 |
+
+### 未验证项强制披露(L0)
+
+交付中凡无法验证、降级处理或长任务部分失败的条目,必须在「已知限制/未验证项/部分失败明细」中显式列出,禁止隐藏。长任务另须附「已完成(带证据) + 未完成(下一步) + 检查点位置」。未声明证据类型的验证视为未完成(见 Step 4)。
+
+## 项目启动模板
+
+软件/网站项目总控第一步必填;其他多文件复杂任务启动前建议填写:
+
+```markdown
+## 项目启动信息
+
+- **项目名称**:
+- **初始需求**:(用户原始需求描述)
+- **技术栈**:
+- **是否存在终极功能**:是 / 否
+- **终极功能定义**:(如有,可验证的一句话描述)
+- **技术约束**:(性能要求/兼容性/安全约束等)
+- **默认循环轮次**:3
+- **安全最大轮次**:6
+- **每轮最大改动点数**:3
+- **角色配置**:主控 + 架构师 + 程序员 + 测试员
+```
+
+## 常见问题答疑(FAQ)
+
+常见疑问、执行禁区、验证失败处置与边界外请求详见 [FAQ.md](./FAQ.md),不确定时优先查阅。

+ 6 - 0
.claude/skills/dev-expert/_meta.json

@@ -0,0 +1,6 @@
+{
+  "ownerId": "448977",
+  "publishedAt": 1786789682501,
+  "slug": "dev-expert",
+  "version": "1.0.48"
+}

+ 29 - 0
.claude/skills/dev-expert/hooks.capture-draft.json

@@ -0,0 +1,29 @@
+{
+  "_comment": "捕获型 hook 草案(跨工具通用,增量配置,不合并进 hooks.json)。matcher 并集覆盖 CodeBuddy/Trae/Cursor 写盘动作 + 命令执行动作;Claude Code/Codex/OpenClaw 可额外在 UserPromptSubmit 挂 capture_learning.py --mode correction 以捕获用户纠错(最准)。确认后由 install_hooks.py 替换占位符并并入 hooks.json 的 PostToolUse 段,不影响现有 15 个防护 hook。",
+  "hooks": {
+    "PostToolUse": [
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit|Bash|execute_command|run_command|run_in_terminal|terminal",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/capture_learning.py\"",
+            "timeout": 15
+          }
+        ]
+      }
+    ],
+    "UserPromptSubmit": [
+      {
+        "matcher": "",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/capture_learning.py\" --mode correction",
+            "timeout": 15
+          }
+        ]
+      }
+    ]
+  }
+}

+ 161 - 0
.claude/skills/dev-expert/hooks.json

@@ -0,0 +1,161 @@
+{
+  "_comment": "通用示例配置(占位符模板 hooks.json)。command 用 {{PYTHON_BIN}} 与 {{HOOKS_DIR}} 占位符,由运行时环境变量替换(方式 A),或经 hooks/install_hooks.py 预处理生成 hooks.installed.json(方式 B)。本模板不写死任何机器路径,换机/换项目只改环境变量或重跑安装器即可。matcher 已并集 write_to_file|replace_in_file|Write|Edit,跨工具(CodeBuddy CN / Trae / Cursor 等)通用。",
+  "hooks": {
+    "PreToolUse": [
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/backup_on_write.py\"",
+            "timeout": 10
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/guard_dirs.py\"",
+            "timeout": 10
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/plan_guard.py\"",
+            "timeout": 10
+          }
+        ]
+      }
+    ],
+    "PostToolUse": [
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/lint_on_write.py\"",
+            "timeout": 30
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/php8_compat.py\"",
+            "timeout": 20
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/security_scan.py\"",
+            "timeout": 20
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/secret_scan.py\"",
+            "timeout": 20
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/utf8_check.py\"",
+            "timeout": 10
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/debug_residue.py\"",
+            "timeout": 10
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/select_star.py\"",
+            "timeout": 10
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/dep_scan.py\"",
+            "timeout": 10
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/sql_injection_check.py\"",
+            "timeout": 15
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/cms_risk_check.py\"",
+            "timeout": 15
+          }
+        ]
+      },
+      {
+        "matcher": "write_to_file|replace_in_file|Write|Edit|Bash|execute_command|run_command|run_in_terminal|terminal",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/capture_learning.py\"",
+            "timeout": 15
+          }
+        ]
+      }
+    ],
+    "PreCompact": [
+      {
+        "matcher": "*",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "{{PYTHON_BIN}} \"{{HOOKS_DIR}}/handoff_snapshot.py\"",
+            "timeout": 20
+          }
+        ]
+      }
+    ]
+  }
+}

+ 100 - 0
.claude/skills/dev-expert/hooks/backup_on_write.py

@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PreToolUse hook (GLOBAL): 在 AI 写/改已存在的源码文件前,自动备份为 <file>.bak。
+
+项目无关:仅依据 stdin 传入的文件路径做备份,可在任意项目中生效。
+设计原则(对齐本项目 Rules 铁律:无 Git,.bak 是唯一回滚手段):
+- 非阻塞:任何情况下都 exit 0,绝不影响原工具执行。
+- 仅对「已存在」的源码类文件生效;新文件、二进制、敏感目录一律跳过。
+- 若 .bak 已存在且与当前文件内容一致,则跳过,避免无意义覆盖/抖动。
+- 多版本备份:保留最近 MAX_VERSIONS 个历史版本(.bak / .bak.1 / .bak.2),
+  每次备份前轮转旧版本,超出上限的自动删除。
+- 备份失败也静默放行(exit 0),不让备份逻辑阻断用户工作。
+"""
+import sys
+import os
+import json
+import shutil
+import filecmp
+
+# 保留最近 N 个历史版本(.bak / .bak.1 / .bak.2)
+MAX_VERSIONS = 10
+
+# 仅备份源码/配置类文件,避免 .bak 污染图片等二进制
+BACKUP_EXT = {
+    ".php", ".html", ".htm", ".js", ".css", ".json", ".py", ".txt",
+    ".md", ".conf", ".xml", ".sql", ".inc", ".tpl", ".vue", ".ts",
+    ".tsx", ".jsx", ".yml", ".yaml", ".ini", ".htaccess",
+}
+
+# 跳过这些目录(对齐 Rules §7.3 排查/忽略清单)
+EXCLUDE_DIRS = {
+    "backup", "vendor", "runtime", "node_modules", "uploads",
+    ".git", ".codebuddy", "ecachefiles",
+}
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+
+    path = os.path.abspath(path)
+    norm = path.replace("\\", "/").lower()
+
+    # 不备份 .bak / .broken 自身
+    if norm.endswith(".bak") or norm.endswith(".broken"):
+        return 0
+    # 跳过敏感/忽略目录
+    if any(part in EXCLUDE_DIRS for part in norm.split("/")):
+        return 0
+    # 仅备份源码/配置类扩展名
+    if os.path.splitext(norm)[1] not in BACKUP_EXT:
+        return 0
+    # 新文件无需回滚
+    if not os.path.isfile(path):
+        return 0
+
+    bak = path + ".bak"
+    try:
+        # 内容一致则跳过(无需备份也无需轮转)
+        if os.path.exists(bak) and filecmp.cmp(bak, path, shallow=False):
+            return 0
+
+        # 轮转旧备份:.bak.(N-1) → .bak.N,删除超出 MAX_VERSIONS 的
+        # 从最旧开始处理,避免覆盖
+        oldest = "%s.bak.%d" % (path, MAX_VERSIONS - 1)
+        if os.path.exists(oldest):
+            os.remove(oldest)
+        for i in range(MAX_VERSIONS - 2, 0, -1):
+            src = "%s.bak.%d" % (path, i)
+            dst = "%s.bak.%d" % (path, i + 1)
+            if os.path.exists(src):
+                os.rename(src, dst)
+        # .bak → .bak.1
+        if os.path.exists(bak):
+            os.rename(bak, "%s.bak.1" % path)
+
+        # 备份当前文件
+        shutil.copy2(path, bak)
+    except Exception:
+        return 0
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 193 - 0
.claude/skills/dev-expert/hooks/capture_learning.py

@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""跨工具「学习捕获」hook 草案(PostToolUse / UserPromptSubmit 通用)。
+
+借鉴 elf-improving-agent 的 activator.sh + error-detector.sh 设计理念,
+改写为 Python 以兼容 Windows 与所有 Agent 工具(CodeBuddy / Trae / Cursor /
+Claude Code / Codex / OpenClaw),不依赖单一平台的 bash 运行时。
+
+设计原则(与 dev-expert 现有 15 个防护 hook 一致):
+- 跨工具兼容:stdin 事件字段名多源容错(tool_input/filePath/file_path/path、
+  tool_response/tool_output/output/result、prompt/query/user_input)。
+- 非阻塞:默认 exit 0(仅回显提醒文本,交 Agent 判断);--strict 时命中 exit 1(强提醒)。
+- 不自动写文件:遵循 elf「提醒而非写入」+ error-ledger「不经确认不写入」禁忌,
+  仅输出「建议记录」提醒,由 Agent 决定是否落盘到 .ai-memory/。
+- 去重提示:扫描 error_index.md 判定 Recurrence,复现时提示更新 Recurrence-Count。
+
+触发信号:
+- 错误信号(工具/命令输出含关键词):elf 16 个 + dev-expert 语境扩展。
+- 纠错信号(用户输入含句式):中英文清单,仅在 --mode correction 或事件含用户输入时检测。
+
+退出码:
+  0 = 无信号 / 仅提醒(非阻塞默认)
+  1 = 命中信号且 --strict(强提醒,仍不阻断已完成的写入/执行)
+"""
+import sys
+import os
+import json
+import re
+import argparse
+
+# ---- 错误关键词(命令/工具输出扫描)----
+ERROR_PATTERNS = [
+    "error:", "Error:", "ERROR:", "failed", "FAILED",
+    "command not found", "No such file", "Permission denied",
+    "fatal:", "Exception", "Traceback", "npm ERR!",
+    "ModuleNotFoundError", "SyntaxError", "TypeError",
+    "exit code", "non-zero",
+    # dev-expert 语境扩展
+    "Parse error", "Fatal error", "PHP Fatal", "PHP Warning",
+    "断言失败", "assert", "测试失败", "未通过", "GATE-CHECK 不足",
+]
+
+# ---- 用户纠错句式(用户输入扫描)----
+CORRECTION_PATTERNS = [
+    "不对", "错了", "搞混", "实际上", "应该是", "你理解", "你误会",
+    "纠正", "混淆", "你之前",
+    "no, that", "actually", "you're wrong", "that's outdated", "not right",
+    "i said",
+]
+
+
+def read_event():
+    """容错读取 hook 事件 JSON;任何解析失败返回空 dict(静默 exit 0)。"""
+    try:
+        raw = sys.stdin.read()
+    except Exception:
+        return {}
+    raw = raw.lstrip("\ufeff")  # 容忍 UTF-8 BOM(项目 .md 常见)
+    if not raw.strip():
+        return {}
+    try:
+        return json.loads(raw)
+    except Exception:
+        return {}
+
+
+def pick(data, *keys, default=""):
+    """从多源字段名中取第一个非空字符串。"""
+    for k in keys:
+        v = data.get(k)
+        if isinstance(v, str) and v.strip():
+            return v
+    return default
+
+
+def get_output(data):
+    return pick(data, "tool_response", "tool_output", "output", "result", "stdout", default="")
+
+
+def get_prompt(data):
+    return pick(data, "prompt", "query", "user_input", "input", default="")
+
+
+def get_tool_name(data):
+    return pick(data, "tool_name", "toolName", "name", default="")
+
+
+def scan_error(text):
+    """返回命中的首个错误关键词,或 None。"""
+    for p in ERROR_PATTERNS:
+        if p in text:
+            return p
+    return None
+
+
+def scan_correction(text):
+    """返回命中的首个纠错句式,或 None。"""
+    low = text.lower()
+    for p in CORRECTION_PATTERNS:
+        if p.lower() in low:
+            return p
+    return None
+
+
+def find_recurrence(index_path, keyword):
+    """扫描 error_index.md,返回含该关键词的已有 ERR-ID 列表(去重提示用)。"""
+    if not index_path or not os.path.isfile(index_path):
+        return []
+    try:
+        with open(index_path, encoding="utf-8") as f:
+            content = f.read()
+    except OSError:
+        return []
+    hits = []
+    for line in content.splitlines():
+        if "|" not in line:
+            continue
+        if keyword.lower() in line.lower():
+            m = re.search(r"ERR-\d+", line)
+            if m and m.group(0) not in hits:
+                hits.append(m.group(0))
+    return hits
+
+
+def main():
+    parser = argparse.ArgumentParser(description="跨工具学习捕获 hook(草案)")
+    parser.add_argument(
+        "--mode", choices=["auto", "correction"], default="auto",
+        help="auto=扫描输出+输入; correction=仅扫描用户输入纠错",
+    )
+    parser.add_argument(
+        "--strict", action="store_true",
+        help="命中信号时 exit 1(强提醒,仍不阻断)",
+    )
+    parser.add_argument(
+        "--index", default="",
+        help="error_index.md 路径(默认:从 PROJECT_ROOT 推断 .ai-memory/error_index.md)",
+    )
+    args = parser.parse_args()
+
+    data = read_event()
+    if not data:
+        return 0
+
+    tool_name = get_tool_name(data)
+    output = get_output(data)
+    prompt = get_prompt(data)
+
+    signal_kind = None
+    signal_word = None
+    snippet = ""
+
+    if args.mode == "correction":
+        c = scan_correction(prompt)
+        if c:
+            signal_kind, signal_word, snippet = "correction", c, prompt[:200]
+    else:
+        e = scan_error(output)
+        if e:
+            signal_kind, signal_word, snippet = "error", e, output[:200]
+        else:
+            c = scan_correction(prompt)
+            if c:
+                signal_kind, signal_word, snippet = "correction", c, prompt[:200]
+
+    if not signal_kind:
+        return 0
+
+    # 去重 / Recurrence 提示
+    index_path = args.index
+    if not index_path:
+        proj = os.environ.get("PROJECT_ROOT", "")
+        if proj:
+            cand = os.path.join(proj, ".ai-memory", "error_index.md")
+            if os.path.isfile(cand):
+                index_path = cand
+    hits = find_recurrence(index_path, signal_word) if index_path else []
+
+    print("[LEARN-CAPTURE] 检测到可提炼信号(建议记录到 error-ledger)")
+    print("  类型: %s | 关键词: %s" % (signal_kind, signal_word))
+    print("  工具: %s" % (tool_name or "unknown"))
+    print("  片段: %s" % snippet.replace("\n", " ").strip())
+    if hits:
+        print("  复现提示: 已记录 %s,建议更新其 Recurrence-Count" % ", ".join(hits))
+    else:
+        print("  复现提示: 未见历史记录,建议新建 ERR-XXX 并附 Recurrence-Count: 1")
+    print("  动作: 在 .ai-memory/error_index.md 追加索引 + errors/ERR-XXX.md 单条(遵循 error-ledger 禁忌)")
+
+    return 1 if args.strict else 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 502 - 0
.claude/skills/dev-expert/hooks/cms_risk_check.py

@@ -0,0 +1,502 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook: 国内常用 CMS(帝国/织梦/Discuz/WordPress/PHPCMS)高危风险检测。
+
+检测 5 类风险:
+1. 文件包含漏洞:include/require + $_GET/$_POST/$_REQUEST/$_COOKIE
+2. 命令执行:eval/system/exec/passthru/shell_exec/popen/proc_open/assert + 用户输入
+3. 反射型 XSS:echo/print/printf + $_GET/$_POST/$_REQUEST 未转义
+4. 任意文件上传:move_uploaded_file 目标路径不可控
+5. SSRF/远程包含:include/require + http:// 或 https://
+
+安全模式(不报警):
+- htmlspecialchars/htmlentities/esc_html/esc_url/e() 转义函数
+- intval/floatval 强制类型转换
+- 常量/配置变量(如 DIR_FS_*, __DIR__)
+"""
+import sys
+import os
+import json
+import re
+
+# 仅检查 PHP 文件(国内 CMS 主要语言)
+TARGET_EXT = ".php"
+
+# 用户输入源(PHP 超全局变量)
+USER_INPUT = r"\$_(?:GET|POST|REQUEST|COOKIE|SERVER)\b"
+
+# 转义/安全函数(同行出现则跳过)
+SAFE_FUNC = (
+    r"htmlspecialchars", r"htmlentities", r"strip_tags",
+    r"esc_html", r"esc_attr", r"esc_url", r"esc_js",
+    r"intval", r"floatval", r"doubleval",
+    r"\be\(",  # Laravel Blade 的 e() 转义
+    r"wp_kses", r"sanitize_text_field",
+    r"addslashes",
+)
+
+# 风险模式
+PATTERNS = [
+    # 1. 文件包含漏洞
+    {
+        "id": "FILE_INCLUDE",
+        "regex": re.compile(
+            r"(?:include|require|include_once|require_once)\s*\(?\s*"
+            r"(?:\$_(?:GET|POST|REQUEST|COOKIE)\b"
+            r"|\$[a-z_]\w*\s*\.\s*\$_(?:GET|POST|REQUEST|COOKIE)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "文件包含漏洞:include/require 直接使用用户输入,可能导致 LFI/RFI",
+    },
+    # 2. 远程文件包含(SSRF)
+    {
+        "id": "RFI_SSRF",
+        "regex": re.compile(
+            r"(?:include|require|include_once|require_once)\s*\(?\s*"
+            r"['\"]https?://",
+            re.IGNORECASE
+        ),
+        "msg": "远程文件包含/SSRF:include/require 远程 URL,极高风险",
+    },
+    # 3. 命令执行:eval/assert + 用户输入
+    {
+        "id": "CODE_EXEC",
+        "regex": re.compile(
+            r"(?:eval|assert)\s*\(\s*"
+            r"(?:\$_(?:GET|POST|REQUEST|COOKIE)\b"
+            r"|base64_decode\s*\(\s*\$_)",
+            re.IGNORECASE
+        ),
+        "msg": "代码执行漏洞:eval/assert 执行用户输入,极高风险(常见后门)",
+    },
+    # 4. 命令执行:system/exec/passthru/shell_exec/popen/proc_open + 用户输入
+    {
+        "id": "CMD_EXEC",
+        "regex": re.compile(
+            r"(?:system|exec|passthru|shell_exec|popen|proc_open)\s*\(\s*"
+            r"(?:\$_(?:GET|POST|REQUEST|COOKIE)\b"
+            r"|\$[a-z_]\w*\s*\(\s*\$_(?:GET|POST|REQUEST|COOKIE)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "命令执行漏洞:system/exec/passthru 执行用户输入",
+    },
+    # 5. 反射型 XSS:echo/print + $_GET/$_POST 未转义
+    {
+        "id": "XSS_REFLECT",
+        "regex": re.compile(
+            r"(?:echo|print|printf)\b[^;]{0,80}"
+            r"\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "反射型 XSS:直接输出用户输入未转义",
+    },
+    # 6. 任意文件上传:move_uploaded_file + 可控路径
+    {
+        "id": "FILE_UPLOAD",
+        "regex": re.compile(
+            r"move_uploaded_file\s*\([^,]+,\s*"
+            r"(?:\$_(?:GET|POST|REQUEST)\b"
+            r"|\$[a-z_]\w*\s*\.\s*\$_(?:GET|POST|REQUEST)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "任意文件上传:move_uploaded_file 目标路径含用户输入",
+    },
+    # 7. SQL 拼接 + 帝国CMS 特有 $empire->query 拼接
+    {
+        "id": "CMS_SQL_QUERY",
+        "regex": re.compile(
+            r"(?:empire->query|empire->fetch|db->query|db->fetchAll)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "CMS SQL 注入:帝国/织梦 DB 对象拼接用户输入",
+    },
+    # 8. 织梦 DedeCMS 特有:$dsql->ExecuteNoneQuery 拼接
+    {
+        "id": "DEDESQL",
+        "regex": re.compile(
+            r"dsql->(?:ExecuteNoneQuery|Execute|GetOne)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "织梦 DedeCMS SQL 注入:$dsql 拼接用户输入",
+    },
+    # 9. Discuz! 特有:DB::query 拼接
+    {
+        "id": "DISCUZSQL",
+        "regex": re.compile(
+            r"DB::query\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Discuz! SQL 注入:DB::query 拼接用户输入",
+    },
+    # 10. WordPress 特有:$wpdb->query 拼接
+    {
+        "id": "WPSQL",
+        "regex": re.compile(
+            r"wpdb->(?:query|get_results|get_var|get_row|get_col)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "WordPress SQL 注入:$wpdb->query/get_results 拼接用户输入",
+    },
+    # 11. PHPCMS 特有:$this->db->query / pc_base::load_model 拼接
+    {
+        "id": "PHPCMS_SQL",
+        "regex": re.compile(
+            r"(?:this->db->query|this->db->select|this->db->get_one)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "PHPCMS SQL 注入:$this->db->query/select 拼接用户输入",
+    },
+    # 12. Drupal 特有:db_query / db_select 拼接
+    {
+        "id": "DRUPAL_SQL",
+        "regex": re.compile(
+            r"(?:db_query|db_select|db_delete|db_update|db_insert|db_merge)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Drupal SQL 注入:db_query/db_select 拼接用户输入",
+    },
+    # 13. Joomla 特有:$db->setQuery / $db->loadResult 拼接
+    {
+        "id": "JOOMLA_SQL",
+        "regex": re.compile(
+            r"db->(?:setQuery|loadResult|loadObjectList|loadAssocList|loadRow)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Joomla SQL 注入:$db->setQuery/loadResult 拼接用户输入",
+    },
+    # 14. ThinkPHP 特有:Db::query / M()->query / $model->query 拼接
+    {
+        "id": "THINKPHP_SQL",
+        "regex": re.compile(
+            r"(?:Db::query|Db::execute|M\(\w+\)->query|model->query|model->execute)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "ThinkPHP SQL 注入:Db::query/M()->query 拼接用户输入",
+    },
+    # 15. CodeIgniter 特有:$this->db->query 拼接
+    {
+        "id": "CI_SQL",
+        "regex": re.compile(
+            r"this->db->query\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "CodeIgniter SQL 注入:$this->db->query 拼接用户输入",
+    },
+    # 16. Laravel 特有:DB::select / DB::statement 拼接(非预处理方式)
+    {
+        "id": "LARAVEL_SQL",
+        "regex": re.compile(
+            r"DB::(?:select|statement|unprepared)\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Laravel SQL 注入:DB::select/statement/unprepared 拼接用户输入",
+    },
+    # 17. Yii 特有:Yii::$app->db->createCommand 拼接
+    {
+        "id": "YII_SQL",
+        "regex": re.compile(
+            r"db->createCommand\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Yii SQL 注入:createCommand 拼接用户输入",
+    },
+    # 18. Symfony 特有:$conn->executeQuery 拼接
+    {
+        "id": "SYMFONY_SQL",
+        "regex": re.compile(
+            r"conn->(?:executeQuery|executeUpdate|executeStatement)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Symfony SQL 注入:executeQuery 拼接用户输入",
+    },
+    # 19. Typecho 特有:$db->query / $db->fetchRow 拼接
+    {
+        "id": "TYPECHO_SQL",
+        "regex": re.compile(
+            r"db->(?:query|fetchRow|fetchALL)\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Typecho SQL 注入:$db->query/fetchRow 拼接用户输入",
+    },
+    # 20. Z-Blog 特有:$zbp->db->query 拼接
+    {
+        "id": "ZBLOG_SQL",
+        "regex": re.compile(
+            r"zbp->db->query\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Z-Blog SQL 注入:$zbp->db->query 拼接用户输入",
+    },
+    # 21. emlog 特有:$db->query / $db->fetch_array 拼接
+    {
+        "id": "EMLOG_SQL",
+        "regex": re.compile(
+            r"\bdb->(?:query|fetch_array|fetch_row|num_rows)\s*\(\s*"
+            r"[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "emlog SQL 注入:$db->query 拼接用户输入",
+    },
+    # 22. PDO 通用:$pdo->query / $pdo->exec 拼接(非预处理方式)
+    {
+        "id": "PDO_SQL",
+        "regex": re.compile(
+            r"pdo->(?:query|exec)\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "PDO SQL 注入:$pdo->query/exec 拼接用户输入(应使用 prepare)",
+    },
+    # 23. mysqli 通用:mysqli_query / $mysqli->query 拼接
+    {
+        "id": "MYSQLI_SQL",
+        "regex": re.compile(
+            r"(?:mysqli_query|mysqli->query)\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "mysqli SQL 注入:mysqli_query 拼接用户输入(应使用 bind_param)",
+    },
+    # 24. mysql 通用(已废弃):mysql_query 拼接
+    {
+        "id": "MYSQL_DEPRECATED_SQL",
+        "regex": re.compile(
+            r"mysql_query\s*\(\s*[\"'].*?\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "mysql_query SQL 注入:已废弃 API + 拼接用户输入(极高风险)",
+    },
+    # === 框架特有风险(非 SQL 注入)===
+    # 25. 反序列化漏洞:unserialize + 用户输入(所有 PHP 框架通用)
+    {
+        "id": "DESERIALIZE",
+        "regex": re.compile(
+            r"unserialize\s*\(\s*(?:\$_(?:GET|POST|REQUEST|COOKIE)\b"
+            r"|file_get_contents\s*\(\s*\$_)",
+            re.IGNORECASE
+        ),
+        "msg": "反序列化漏洞:unserialize 用户输入,可能导致 RCE",
+    },
+    # 26. 模板注入 - ThinkPHP:display/assign/fetch + 用户输入
+    {
+        "id": "TP_TEMPLATE_INJECT",
+        "regex": re.compile(
+            r"(?:this->assign|this->display|this->fetch)\s*\([^)]{0,80}"
+            r"\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "ThinkPHP 模板注入:assign/display/fetch 含用户输入",
+    },
+    # 27. 模板注入 - Twig/Symfony:render + 用户输入
+    {
+        "id": "TWIG_INJECT",
+        "regex": re.compile(
+            r"(?:render|renderView|Twig::render)\s*\([^)]{0,80}"
+            r"\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Twig 模板注入:render 含用户输入",
+    },
+    # 28. 模板注入 - Blade/Laravel:Blade::compileString + 用户输入
+    {
+        "id": "BLADE_INJECT",
+        "regex": re.compile(
+            r"Blade::compileString\s*\([^)]{0,80}\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Laravel Blade 模板注入:compileString 含用户输入",
+    },
+    # 29. SSRF:file_get_contents/curl_exec + 用户输入 URL
+    {
+        "id": "SSRF",
+        "regex": re.compile(
+            r"(?:file_get_contents|curl_exec|fopen|readfile)\s*\(\s*"
+            r"(?:\$_(?:GET|POST|REQUEST)\b"
+            r"|\$[a-z_]\w*\s*\.\s*\$_(?:GET|POST|REQUEST)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "SSRF/任意文件读取:file_get_contents/curl_exec 含用户输入",
+    },
+    # 30. XXE:simplexml_load_string/DOMDocument + 用户输入
+    {
+        "id": "XXE",
+        "regex": re.compile(
+            r"(?:simplexml_load_string|DOMDocument|XMLReader|xml_parse)\s*\(\s*"
+            r"(?:\$_(?:GET|POST|REQUEST)\b"
+            r"|\$[a-z_]\w*\s*\.\s*\$_(?:GET|POST|REQUEST)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "XXE 漏洞:XML 解析器含用户输入,可能读取服务器文件",
+    },
+    # 31. 代码执行:preg_replace /e 修饰符(PHP < 7)
+    {
+        "id": "PREG_E_EXEC",
+        "regex": re.compile(
+            r"preg_replace\s*\([^)]*['\"][^'\"]*[a-z]?e[a-z]?['\"]",
+            re.IGNORECASE
+        ),
+        "msg": "代码执行:preg_replace 使用 /e 修饰符(PHP<7 代码执行)",
+    },
+    # 32. 代码执行:create_function(PHP < 8,已废弃)
+    {
+        "id": "CREATE_FUNCTION",
+        "regex": re.compile(
+            r"create_function\s*\(",
+            re.IGNORECASE
+        ),
+        "msg": "代码执行:create_function 已废弃(PHP 8+ 移除),存在注入风险",
+    },
+    # 33. ThinkPHP 特有:I() 函数未过滤直接入 SQL
+    {
+        "id": "TP_I_SQL",
+        "regex": re.compile(
+            r"(?:empire->query|db->query|Db::query)\s*\([^)]{0,80}"
+            r"\bI\(['\"](?:get|post|request|param)\.",
+            re.IGNORECASE
+        ),
+        "msg": "ThinkPHP SQL 注入:I() 函数结果直接入 SQL(需 where 条件绑定)",
+    },
+    # 34. Laravel 特有:Artisan::call + 用户输入(命令注入)
+    {
+        "id": "ARTISAN_INJECT",
+        "regex": re.compile(
+            r"Artisan::call\s*\([^)]{0,80}\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "Laravel 命令注入:Artisan::call 含用户输入",
+    },
+    # 35. CodeIgniter 特有:$this->load->view + 用户输入模板名
+    {
+        "id": "CI_VIEW_INJECT",
+        "regex": re.compile(
+            r"this->load->view\s*\(\s*\$_(?:GET|POST|REQUEST)\b",
+            re.IGNORECASE
+        ),
+        "msg": "CodeIgniter 模板注入:load->view 含用户输入模板名",
+    },
+    # 36. Yii 特有:unserialize + 用户输入(Yii 反序列化漏洞高发)
+    {
+        "id": "YII_DESERIALIZE",
+        "regex": re.compile(
+            r"unserialize\s*\(\s*(?:\$request->(?:get|post|body)\b"
+            r"|\$_(?:GET|POST|REQUEST)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "Yii 反序列化漏洞:unserialize 用户输入",
+    },
+    # 37. 文件写入 + 用户输入路径(任意文件写入)
+    {
+        "id": "FILE_WRITE",
+        "regex": re.compile(
+            r"(?:file_put_contents|fwrite|fputs)\s*\(\s*"
+            r"(?:\$_(?:GET|POST|REQUEST)\b"
+            r"|\$[a-z_]\w*\s*\.\s*\$_(?:GET|POST|REQUEST)\b)",
+            re.IGNORECASE
+        ),
+        "msg": "任意文件写入:file_put_contents/fwrite 路径含用户输入",
+    },
+]
+
+# 安全函数正则(用于过滤误报)
+SAFE_RE = re.compile("|".join(SAFE_FUNC), re.IGNORECASE)
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+
+    if not path.lower().endswith(TARGET_EXT):
+        return 0
+
+    try:
+        with open(path, encoding="utf-8", errors="ignore") as f:
+            lines = f.readlines()
+    except OSError:
+        return 0
+
+    # 中间变量污点追踪:收集被赋值为用户输入的变量,用于跨行 LFI 检测
+    #   $page = $_GET['page']; include($page);
+    taint_assign_re = re.compile(
+        r"\$([A-Za-z_]\w*)\s*=\s*"
+        r"(?:\$_(?:GET|POST|REQUEST|COOKIE)\b"
+        r"|\$[A-Za-z_]\w*\s*\.\s*\$_(?:GET|POST|REQUEST|COOKIE)\b)",
+        re.IGNORECASE,
+    )
+    tainted_vars = set()
+    for line in lines:
+        for m in taint_assign_re.finditer(line):
+            tainted_vars.add(m.group(1))
+
+    hits = []
+    for i, line in enumerate(lines, 1):
+        # 跳过注释行
+        stripped = line.strip()
+        if stripped.startswith("//") or stripped.startswith("#"):
+            continue
+        if stripped.startswith("/*") or stripped.startswith("*"):
+            continue
+
+        # 跳过包含安全函数的行
+        if SAFE_RE.search(line):
+            continue
+
+        for pat in PATTERNS:
+            if pat["regex"].search(line):
+                hits.append((i, pat["id"], pat["msg"], stripped[:120]))
+
+    # 中间变量 LFI:include/require 的参数是被用户输入赋值过的变量
+    if tainted_vars:
+        inter_include_re = re.compile(
+            r"(?:include|require|include_once|require_once)\s*\(?\s*"
+            r"\$(" + "|".join(re.escape(v) for v in sorted(tainted_vars, key=len, reverse=True)) + r")\b",
+            re.IGNORECASE,
+        )
+        for i, line in enumerate(lines, 1):
+            stripped = line.strip()
+            if stripped.startswith("//") or stripped.startswith("#"):
+                continue
+            if stripped.startswith("/*") or stripped.startswith("*"):
+                continue
+            if SAFE_RE.search(line):
+                continue
+            if inter_include_re.search(line):
+                hits.append((i, "FILE_INCLUDE",
+                    "文件包含漏洞:include/require 使用被用户输入赋值过的中间变量,可能导致 LFI/RFI",
+                    stripped[:120]))
+
+    if not hits:
+        return 0
+
+    print("[CMS-RISK] 检测到 %d 处 CMS 高危风险 (%s):" % (len(hits), path))
+    for line_no, risk_id, msg, code in hits[:20]:
+        print("  L%d [%s] %s" % (line_no, risk_id, msg))
+        print("    > %s" % code)
+    if len(hits) > 20:
+        print("  ... 还有 %d 处" % (len(hits) - 20))
+    print("[CMS-RISK] 建议使用预处理语句/参数绑定/转义函数修复")
+    return 1
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 65 - 0
.claude/skills/dev-expert/hooks/debug_residue.py

@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写源码后扫描调试残留。
+
+对齐 Rules §10(错误闭环/勿留调试):捕获 var_dump/print_r/var_export/die()/exit()/console.log。
+仅对文本源文件生效,跳过 tests 目录(测试辅助允许)。非阻断 exit 1 回显 AI。
+"""
+import sys
+import os
+import json
+import re
+
+TEXT_EXT = {".php", ".html", ".htm", ".js", ".css", ".py", ".json", ".md", ".txt"}
+DEBUG = re.compile(
+    r"(?:var_dump|print_r|var_export|\bdie\b|(?<!\.)\bexit\b|console\s*\.\s*log)\s*\("
+)
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() not in TEXT_EXT:
+        return 0
+    # 跳过测试目录,避免误报测试辅助代码
+    if any(s.lower() in ("tests", "test") for s in path.split(os.sep)):
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        with open(path, "r", encoding="utf-8", errors="ignore") as f:
+            text = f.read()
+    except Exception:
+        return 0
+
+    hits = []
+    for m in DEBUG.finditer(text):
+        line = text.count("\n", 0, m.start()) + 1
+        hits.append((line, m.group(0).strip()))
+    if hits:
+        print("[DEBUG-WARN] 发现调试残留,请确认是否应保留(Rules §10):")
+        for line, tok in hits:
+            print("  行 %d: %s" % (line, tok))
+        return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 74 - 0
.claude/skills/dev-expert/hooks/dep_scan.py

@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 依赖变更后提示漏洞扫描。
+
+检测到依赖清单文件被修改时,输出对应包管理器的漏洞扫描命令提示,
+帮助 AI 在安装/更新依赖后主动检测已知漏洞(npm audit / pip-audit / composer audit)。
+
+触发文件:
+  - package.json / package-lock.json → npm audit
+  - requirements.txt / Pipfile / pyproject.toml → pip-audit
+  - composer.json / composer.lock → composer audit
+  - go.mod / go.sum → govulncheck
+  - pom.xml → mvn dependency-check
+  - Gemfile / Gemfile.lock → bundle audit
+
+输出为提示信息(exit 0),不阻断执行;AI 应根据提示决定是否运行扫描。
+"""
+import sys
+import os
+import json
+
+# 依赖文件 → 扫描命令映射
+DEP_FILES = {
+    "package.json": "npm audit --audit-level=moderate",
+    "package-lock.json": "npm audit --audit-level=moderate",
+    "yarn.lock": "yarn audit --level moderate",
+    "pnpm-lock.yaml": "pnpm audit --audit-level moderate",
+    "requirements.txt": "pip-audit -r requirements.txt",
+    "pipfile": "pip-audit",
+    "pipfile.lock": "pip-audit",
+    "pyproject.toml": "pip-audit",
+    "poetry.lock": "pip-audit",
+    "composer.json": "composer audit",
+    "composer.lock": "composer audit",
+    "go.mod": "govulncheck ./...",
+    "go.sum": "govulncheck ./...",
+    "pom.xml": "mvn dependency-check:check",
+    "gemfile": "bundle audit check",
+    "gemfile.lock": "bundle audit check",
+}
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+
+    basename = os.path.basename(path).lower()
+    scan_cmd = DEP_FILES.get(basename)
+    if not scan_cmd:
+        return 0
+
+    print("[DEP-SCAN] 检测到依赖文件变更: %s" % basename)
+    print("建议执行漏洞扫描: %s" % scan_cmd)
+    print("若存在已知漏洞,应在交付前在「已知限制/未验证项」中披露。")
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 47 - 0
.claude/skills/dev-expert/hooks/guard_dirs.py

@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PreToolUse hook (GLOBAL): 拦截对数据/依赖目录的写操作。
+
+对齐 Rules §5/§7.3:uploads(上传目录,禁手改源码)、backup(回滚存放)、
+vendor/node_modules(依赖) 不应由 AI 直接编辑源码写入。命中即 exit 2 阻断。
+- 仅检查目标文件路径本身所在目录,不误伤其父级正常目录。
+- 输出清晰拒绝原因,便于 AI 立即感知并改向。
+"""
+import sys
+import os
+import json
+
+# 禁止 AI 直接写入的目录(数据/依赖/回滚,大小写不敏感)
+FORBIDDEN = {"uploads", "backup", "vendor", "node_modules"}
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+
+    norm = os.path.abspath(path).replace("\\", "/").lower()
+    if any(part in FORBIDDEN for part in norm.split("/")):
+        print("[GUARD-BLOCK] 拒绝写入受保护目录:%s" % path)
+        print("该目录(uploads/backup/vendor/node_modules)不应由 AI 直接编辑源码,"
+              "如确需改动请先在计划中明确并说明理由。")
+        return 2  # 阻断
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 190 - 0
.claude/skills/dev-expert/hooks/handoff_snapshot.py

@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PreCompact hook (GLOBAL): 上下文压缩前写 ≤2KB 交接快照到项目记忆根。
+
+落点:{PROJECT_ROOT}/.ai-memory/_handoff_snapshot.md
+(PROJECT_ROOT 按 project-memory-management「路径根目录探测协议」解析,绝
+不写死 .codebuddy 等其它 IDE 约定路径;记忆跟随项目,跨 IDE 通用。)
+
+hook 拿不到对话内容,故快照基于文件系统派生(压缩后最实用的重定位信息):
+- 近 4 小时内被改动的项目文件(排除备份/依赖/缓存/记忆目录),按时间倒序取前 25
+- 当前 Plan/*.md 的状态行(## 状态)
+- 最新一份每日 memory 文件名
+"""
+import sys
+import os
+import json
+import time
+
+CUTOFF_HOURS = 4
+MAX_FILES = 25
+EXCLUDE = {"backup", "vendor", "node_modules", "uploads",
+           ".git", ".codebuddy", ".ai-memory", "ecachefiles",
+           "__pycache__", "dist", "build", "cache", "tmp", "temp", "logs", "runtime"}
+# 排除备份/临时文件扩展名(避免 backup_on_write 的 .bak/.bak.1/.bak.2 污染快照)
+EXCLUDE_EXT = {".bak", ".tmp", ".log", ".cache", ".pyc", ".swp", ".swo"}
+
+
+def _find_git_root(start):
+    """向上找最近的 .git 目录,返回其所在目录(仓库根),找不到返回 None。"""
+    d = os.path.abspath(start)
+    while True:
+        if os.path.isdir(os.path.join(d, ".git")):
+            return d
+        nd = os.path.dirname(d)
+        if nd == d:
+            return None
+        d = nd
+
+
+def resolve_project_root(root):
+    """按技能「路径根目录探测协议」解析 PROJECT_ROOT:
+
+    1. 环境变量 PROJECT_ROOT(跨 IDE 通用,优先):绝对路径直接用,相对路径按 root 拼接。
+    2. Git 仓库根目录:向上找含 .git 的目录。
+    3. IDE 工作区根目录:仅当 cwd 自身已含 .ai-memory(确为项目记忆根)时才回退。
+    4. 以上均未命中:hook 无法询问用户,返回 None(由调用方跳过,严禁自行创建目录)。
+    """
+    env = os.environ.get("PROJECT_ROOT")
+    if env:
+        return env if os.path.isabs(env) else os.path.join(root, env)
+    git_root = _find_git_root(root)
+    if git_root:
+        return git_root
+    if os.path.isdir(os.path.join(root, ".ai-memory")):
+        return root
+    return None
+
+
+def main():
+    try:
+        sys.stdin.read()
+    except Exception:
+        pass
+
+    cwd = os.getcwd()
+    proj_root = resolve_project_root(cwd)
+    if not proj_root:
+        # 项目根不可解析(如运行在 skill 目录、或记忆在数据库的运行时不落文件系统):跳过落盘
+        return 0
+    mem_dir = os.path.join(proj_root, ".ai-memory")
+    root = proj_root  # 所有扫描与相对路径均以解析出的项目根为基准
+    cutoff = time.time() - CUTOFF_HOURS * 3600
+
+    recent = []
+    for dirpath, dirnames, filenames in os.walk(root):
+        dirnames[:] = [d for d in dirnames
+                       if d.lower() not in EXCLUDE and not d.startswith(".")]
+        for fn in filenames:
+            # 过滤备份/临时文件扩展名
+            ext = os.path.splitext(fn)[1].lower()
+            if ext in EXCLUDE_EXT:
+                continue
+            fp = os.path.join(dirpath, fn)
+            try:
+                mtime = os.path.getmtime(fp)
+            except OSError:
+                continue
+            if mtime >= cutoff:
+                recent.append((mtime, fp))
+    recent.sort(reverse=True)
+    recent = recent[:MAX_FILES]
+
+    def rel(p):
+        return os.path.relpath(p, root).replace("\\", "/")
+
+    lines = ["# 压缩交接快照 (PreCompact 自动生成)", ""]
+    lines.append("生成时间: " + time.strftime("%Y-%m-%d %H:%M:%S"))
+    lines.append("工作区: " + root)
+    lines.append("")
+
+    # 输出顺序:Plan 状态 → 最新记忆 → 改动文件列表(最重要的放前面,避免截断丢失)
+
+    # Plan 状态(兼容 Plan/plan 目录名大小写)
+    lines.append("## 进行中计划 (Plan/*.md 状态)")
+    plan_dir = None
+    for name in ("Plan", "plan"):
+        candidate = os.path.join(root, name)
+        if os.path.isdir(candidate):
+            plan_dir = candidate
+            break
+    if plan_dir:
+        for fn in sorted(os.listdir(plan_dir)):
+            if not fn.endswith("_plan.md"):
+                continue
+            p = os.path.join(plan_dir, fn)
+            status = ""
+            try:
+                with open(p, encoding="utf-8", errors="ignore") as f:
+                    for line in f:
+                        if line.strip().startswith("## 状态"):
+                            status = line.strip()
+                            break
+            except OSError:
+                pass
+            lines.append("- %s  %s" % (fn, status))
+    else:
+        lines.append("- (无 Plan 目录)")
+    lines.append("")
+
+    # 最新 memory(按 mtime 排序,避免非日期格式文件名排序不准)
+    lines.append("## 最新记忆")
+    if os.path.isdir(mem_dir):
+        mds = []
+        for f in os.listdir(mem_dir):
+            if not f.endswith(".md"):
+                continue
+            fp = os.path.join(mem_dir, f)
+            try:
+                mds.append((os.path.getmtime(fp), f))
+            except OSError:
+                continue
+        mds.sort(reverse=True)  # 按 mtime 降序,最新的在前
+        rel_mem = os.path.relpath(mem_dir, root).replace("\\", "/")
+        if mds:
+            lines.append("- 最新: %s/%s" % (rel_mem, mds[0][1]))
+        else:
+            lines.append("- (无)")
+    else:
+        lines.append("- (无 memory 目录)")
+    lines.append("")
+
+    # 改动文件列表(放最后,截断时只丢失文件列表,不丢失 Plan/记忆)
+    lines.append("## 近 %d 小时改动文件 (前 %d)" % (CUTOFF_HOURS, MAX_FILES))
+    if recent:
+        for mtime, fp in recent:
+            lines.append("- %s  (%s)" % (rel(fp),
+                                          time.strftime("%m-%d %H:%M", time.localtime(mtime))))
+    else:
+        lines.append("- (无)")
+
+    out = "\n".join(lines) + "\n"
+    b = out.encode("utf-8")
+    if len(b) > 2048:
+        # 按字节截断并在字符边界安全解码,避免切断多字节中文
+        b = b[:2048]
+        out = b.decode("utf-8", errors="ignore")
+    try:
+        os.makedirs(mem_dir, exist_ok=True)
+        snap = os.path.join(mem_dir, "_handoff_snapshot.md")
+        tmp = snap + ".tmp"
+        # 二进制写入:避免 Windows 文本模式把 \n 转 \r\n 撑大文件致超 2048
+        with open(tmp, "wb") as f:
+            f.write(out.encode("utf-8"))
+        try:
+            os.replace(tmp, snap)  # 原子替换,避免目标被读锁占用时写失败
+        except OSError:
+            # 极少数情况目标被锁:退回直接覆盖写
+            with open(snap, "wb") as f:
+                f.write(out.encode("utf-8"))
+            try:
+                os.remove(tmp)
+            except OSError:
+                pass
+    except OSError:
+        return 0
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 156 - 0
.claude/skills/dev-expert/hooks/install_hooks.py

@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""hooks.json 占位符替换安装器。
+
+将 hooks.json 中的 {{PYTHON_BIN}} 和 {{HOOKS_DIR}} 占位符替换为真实路径,
+生成可直接加载的 hooks 配置,降低对运行时占位符替换能力的依赖。
+
+用法:
+  # 1. 自动探测(推荐):用当前 Python 解释器 + 脚本所在目录作为 HOOKS_DIR
+  python install_hooks.py
+
+  # 2. 通过命令行参数指定
+  python install_hooks.py --python-bin /usr/bin/python3 --hooks-dir /path/to/hooks
+
+  # 3. 通过环境变量指定
+  PYTHON_BIN=/usr/bin/python3 HOOKS_DIR=/path/to/hooks python install_hooks.py
+
+  # 4. 指定输出路径(默认输出到 hooks.installed.json,不覆盖原模板)
+  python install_hooks.py --output /path/to/hooks.final.json
+
+  # 5. 原地覆盖(谨慎使用)
+  python install_hooks.py --in-place
+
+优先级:命令行参数 > 环境变量 > 自动探测
+"""
+import argparse
+import json
+import os
+import shutil
+import sys
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description="hooks.json 占位符替换安装器",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+    parser.add_argument(
+        "--python-bin",
+        default=os.environ.get("PYTHON_BIN", ""),
+        help="Python 解释器路径(默认:当前 sys.executable,或环境变量 PYTHON_BIN)",
+    )
+    parser.add_argument(
+        "--hooks-dir",
+        default=os.environ.get("HOOKS_DIR", ""),
+        help="hook 脚本目录(默认:本脚本所在目录,或环境变量 HOOKS_DIR)",
+    )
+    parser.add_argument(
+        "--input",
+        default=None,
+        help="输入 hooks.json 模板路径(默认:本脚本上级目录的 hooks.json)",
+    )
+    parser.add_argument(
+        "--output",
+        default=None,
+        help="输出路径(默认:与输入同目录的 hooks.installed.json,不覆盖原模板)",
+    )
+    parser.add_argument(
+        "--in-place",
+        action="store_true",
+        help="原地覆盖输入文件(谨慎使用,会丢失模板占位符;自动备份为 .bak)",
+    )
+    args = parser.parse_args()
+
+    script_dir = os.path.dirname(os.path.abspath(__file__))
+
+    # 解析 Python 解释器路径
+    python_bin = args.python_bin or sys.executable
+    if not os.path.isabs(python_bin):
+        python_bin = os.path.abspath(python_bin)
+    if not os.path.isfile(python_bin):
+        print("[INSTALL-HOOKS] 错误:Python 解释器不存在: %s" % python_bin, file=sys.stderr)
+        return 1
+
+    # 解析 hooks 目录
+    hooks_dir = args.hooks_dir or script_dir
+    if not os.path.isabs(hooks_dir):
+        hooks_dir = os.path.abspath(hooks_dir)
+    if not os.path.isdir(hooks_dir):
+        print("[INSTALL-HOOKS] 错误:hooks 目录不存在: %s" % hooks_dir, file=sys.stderr)
+        return 1
+
+    # 解析输入路径(默认上级目录的 hooks.json)
+    input_path = args.input or os.path.abspath(os.path.join(script_dir, "..", "hooks.json"))
+    if not os.path.isfile(input_path):
+        print("[INSTALL-HOOKS] 错误:hooks.json 模板不存在: %s" % input_path, file=sys.stderr)
+        return 1
+
+    # 解析输出路径
+    if args.in_place:
+        output_path = input_path
+    else:
+        output_path = args.output or os.path.join(
+            os.path.dirname(input_path), "hooks.installed.json"
+        )
+        output_path = os.path.abspath(output_path)
+
+    # 读取模板
+    try:
+        with open(input_path, encoding="utf-8") as f:
+            raw = f.read()
+    except OSError as e:
+        print("[INSTALL-HOOKS] 错误:读取模板失败: %s" % e, file=sys.stderr)
+        return 1
+
+    # 替换占位符(Windows 路径反斜杠在 JSON 字符串中需转义)
+    python_bin_escaped = python_bin.replace("\\", "\\\\")
+    hooks_dir_escaped = hooks_dir.replace("\\", "\\\\")
+    replacements = {
+        "{{PYTHON_BIN}}": python_bin_escaped,
+        "{{HOOKS_DIR}}": hooks_dir_escaped,
+    }
+    result = raw
+    for placeholder, value in replacements.items():
+        result = result.replace(placeholder, value)
+
+    # 验证 JSON 合法性
+    try:
+        json.loads(result)
+    except json.JSONDecodeError as e:
+        print("[INSTALL-HOOKS] 错误:替换后 JSON 解析失败: %s" % e, file=sys.stderr)
+        print("[INSTALL-HOOKS] 替换结果片段:", file=sys.stderr)
+        print(result[:500], file=sys.stderr)
+        return 1
+
+    # 原地覆盖时先备份
+    if args.in_place:
+        bak = input_path + ".bak"
+        try:
+            shutil.copy2(input_path, bak)
+            print("[INSTALL-HOOKS] 已备份原模板到: %s" % bak)
+        except OSError as e:
+            print("[INSTALL-HOOKS] 警告:备份失败: %s" % e, file=sys.stderr)
+
+    # 写入输出
+    try:
+        with open(output_path, "w", encoding="utf-8") as f:
+            f.write(result)
+    except OSError as e:
+        print("[INSTALL-HOOKS] 错误:写入输出失败: %s" % e, file=sys.stderr)
+        return 1
+
+    # 摘要
+    print("[INSTALL-HOOKS] 安装成功")
+    print("  模板: %s" % input_path)
+    print("  输出: %s" % output_path)
+    print("  Python: %s" % python_bin)
+    print("  Hooks Dir: %s" % hooks_dir)
+    if not args.in_place:
+        print("")
+        print("下一步:将 %s 接入你的 Agent 运行时 hooks 配置入口" % output_path)
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 111 - 0
.claude/skills/dev-expert/hooks/lint_on_write.py

@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写 PHP 文件后做语法自检。
+
+后端 php -l:本环境 php -l 对语法错误返回退出码 0(异常),但 stdout 明确含
+"Parse error" / "Errors parsing"。故以【输出字符串】判定(非退出码),对真实项目路径可靠闭环。
+hook 跑的是工作区内真实文件(非 tempfile),故输出判定稳定。
+- 仅对 .php 文件生效。
+- 非阻塞:发现语法错误时 exit 1(warning,回显 AI),不阻断已完成的写入。
+- 其他故障(如 php 找不到)静默 exit 0,避免误报卡住工作。
+"""
+import sys
+import os
+import json
+import shutil
+import subprocess
+
+# PHP 可执行文件探测协议(对齐 references/cms-development.md「PHP 可执行文件探测协议」):
+# 严禁硬编码唯一路径,按优先级链解析,换机/换用户/跨平台可用。
+_WIN_CANDIDATES = [
+    r"F:\BtSoft\php\{ver}\php.exe",
+    r"D:\phpstudy\php\{ver}\php.exe",
+    r"C:\xampp\php\php.exe",
+    r"C:\Program Files\php\php.exe",
+]
+_MAC_CANDIDATES = [
+    "/opt/homebrew/bin/php@{ver}",
+    "/usr/local/bin/php@{ver}",
+    "/Applications/MAMP/bin/php/php{ver}/bin/php",
+]
+_LIN_CANDIDATES = [
+    "/usr/bin/php{ver}",
+    "/usr/local/bin/php{ver}",
+    "/opt/php/{ver}/bin/php",
+]
+# 版本号高到低探测(含本机 F:\BtSoft\php\85 等)
+_VERSIONS = ["85", "84", "83", "82", "81", "80", "74"]
+
+
+def resolve_php():
+    """按探测协议解析 PHP 可执行文件路径;全部未命中返回 None(调用方静默跳过)。"""
+    env = os.environ.get("PHP_BIN")
+    if env and os.path.isfile(env):
+        return env
+    # 多版本环境变量(PHP_85 / PHP_82 ...)
+    for ver in _VERSIONS:
+        envv = os.environ.get("PHP_" + ver)
+        if envv and os.path.isfile(envv):
+            return envv
+    # 系统 PATH
+    p = shutil.which("php")
+    if p:
+        return p
+    # 常见安装位置自动扫描
+    for tpl in _WIN_CANDIDATES + _MAC_CANDIDATES + _LIN_CANDIDATES:
+        for ver in _VERSIONS:
+            cand = tpl.format(ver=ver)
+            if os.path.isfile(cand):
+                return cand
+    return None
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() != ".php":
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    php_bin = resolve_php()
+    if not php_bin:
+        # PHP 不可解析(未配置 PHP_BIN 且不在 PATH/常见位置):静默跳过,避免误报卡住工作
+        return 0
+
+    try:
+        r = subprocess.run(
+            [php_bin, "-l", path],
+            capture_output=True, text=True, timeout=30,
+        )
+    except Exception:
+        return 0
+
+    out = (r.stdout or "") + (r.stderr or "")
+    # 本环境 php -l 退出码对语法错误仍为 0(异常),但 stdout 含 Parse error 可靠。
+    # 成功信息 "No syntax errors detected" 不含 "Parse error",不会误判。
+    if "Parse error" in out or "Errors parsing" in out:
+        print("[LINT-FAIL] php -l 发现语法错误:")
+        print(out.strip())
+        return 1  # warning,回显给 AI
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 81 - 0
.claude/skills/dev-expert/hooks/php8_compat.py

@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写 .php 后做 PHP 8.x 兼容性静态扫描(对齐 Rules §6)。
+
+仅做纯模式匹配(不依赖 php -l,因本环境 php -l 退出码不可信),命中即 warning 回显 AI,
+非阻断(exit 0)。涵盖:短标签、裸数组键、each()/create_function()/get_magic_quotes_gpc()。
+"""
+import sys
+import os
+import json
+import re
+
+# <? 后非 php/=/xml 视为短标签(<?php / <?= / <?xml 合法)
+SHORT_TAG = re.compile(r'<\?(?!(php|=|xml)\b)')
+# $arr[key] 裸键(key 为字母开头、非 $ 变量、非引号、非纯数字)
+BARE_KEY = re.compile(r'\$\w+\[([A-Za-z_][A-Za-z0-9_]*)\]')
+BAD_FUNCS = [
+    (re.compile(r'\beach\s*\('), 'each()'),
+    (re.compile(r'\bcreate_function\s*\('), 'create_function()'),
+    (re.compile(r'\bget_magic_quotes_gpc\s*\('), 'get_magic_quotes_gpc()'),
+]
+
+
+def scan(text):
+    hits = []
+    if SHORT_TAG.search(text):
+        hits.append("短标签 <?(非 <?php/<?=/<?xml),须改 <?php")
+    mk = BARE_KEY.search(text)
+    if mk:
+        key = mk.group(1)
+        # 跳过全大写常量(如 $arr[MY_CONST])及语言常量 true/false/null,
+        # 避免误伤合法常量用法;§6 针对的是裸字符串键(小写/混合大小写)
+        if key.isupper() or key.lower() in ("true", "false", "null"):
+            pass
+        else:
+            hits.append("裸数组键 $arr[%s] 未加引号,须 $arr['%s'](Rules §6)" % (key, key))
+    for rx, name in BAD_FUNCS:
+        if rx.search(text):
+            hits.append("已废弃函数 %s(PHP8 已移除),须替换(Rules §6)" % name)
+    return hits
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() != ".php":
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        text = open(path, encoding="utf-8", errors="ignore").read()
+    except Exception:
+        return 0
+
+    hits = scan(text)
+    if hits:
+        print("[PHP8-COMPAT-WARN] 发现 PHP8 兼容隐患:")
+        for h in hits:
+            print("  - " + h)
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 148 - 0
.claude/skills/dev-expert/hooks/plan_guard.py

@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PreToolUse hook (GLOBAL): 改核心目录前强制要求存在进行中 *_plan.md(对齐 Rules §13)。
+
+- 对落在「核心/危险目录」的源码文件写操作拦截;tests/、Plan/、依赖/资源目录不拦。
+- 拦截条件:目标在核心目录 且 其 workspace 的 Plan/ 下无任何状态为 规划中/进行中 的 *_plan.md。
+- 放行条件(有意例外):workspace 未建立 Plan/ 目录(无 Plan 体系)时自动放行,避免误伤不采用本工作流的项目。此行为与「强制规划」措辞一致——§13 面向已采用 Plan 体系的项目。
+- 命中 exit 2 阻断,提示先建计划(Trivial Fix 通道见 Rules §14,但核心目录改动仍强制规划)。
+
+可配置(环境变量,分号分隔,未设置时用默认值):
+- PLAN_GUARD_CORE_DIRS:核心目录名集合,默认 src;app;lib;core;internal;system;engine
+- PLAN_GUARD_EXCLUDE_DIRS:排除目录名集合,默认 tests;Plan;backup;vendor;node_modules;uploads;.codebuddy;dist;build;public;static;assets;test;__pycache__;.git
+- PLAN_GUARD_TARGET_EXTS:拦截的文件扩展名,默认 .php;.js;.ts;.jsx;.tsx;.py;.java;.go;.vue
+
+CMS 项目(如帝国CMS)需设置:
+- PLAN_GUARD_CORE_DIRS=e/class;e/data;e/config;e/mods;e/member;e/template;e/admin
+"""
+import sys
+import os
+import json
+import re
+
+def _split_env(name, default):
+    """从环境变量读取分号分隔的集合,未设置时用默认值。"""
+    val = os.environ.get(name, "")
+    if not val.strip():
+        return default
+    return set(p.strip() for p in val.split(";") if p.strip())
+
+# 核心目录:可通过 PLAN_GUARD_CORE_DIRS 配置;默认为通用源码根目录
+CORE_DIRS = _split_env(
+    "PLAN_GUARD_CORE_DIRS",
+    {"src", "app", "lib", "core", "internal", "system", "engine"},
+)
+# 排除目录:可通过 PLAN_GUARD_EXCLUDE_DIRS 配置;默认为通用排除集合
+EXCLUDE_DIRS = _split_env(
+    "PLAN_GUARD_EXCLUDE_DIRS",
+    {
+        "tests", "test", "Plan", "backup", "vendor", "node_modules",
+        "uploads", ".codebuddy", "dist", "build", "public",
+        "static", "assets", "__pycache__", ".git",
+    },
+)
+# 拦截的扩展名:可通过 PLAN_GUARD_TARGET_EXTS 配置
+TARGET_EXTS = _split_env(
+    "PLAN_GUARD_TARGET_EXTS",
+    {".php", ".js", ".ts", ".jsx", ".tsx", ".py", ".java", ".go", ".vue"},
+)
+
+# 状态标题行:兼容两种写法,与 handoff_snapshot.py 判定统一。
+#   ①「## 状态」独占一行、状态值写在下一行(§13 canonical 模板)
+#   ②「## 状态: 进行中」状态值同行带在冒号后(delivery-assurance.md 写法)
+# 不再用严格正则锚定行尾,避免漏匹配「同行带值」格式导致误拦。
+
+
+def find_workspace(path):
+    """向上找含 Plan/ 子目录的祖先目录作为 workspace。"""
+    d = os.path.dirname(path)
+    while True:
+        if os.path.isdir(os.path.join(d, "Plan")):
+            return d
+        nd = os.path.dirname(d)
+        if nd == d:
+            return None
+        d = nd
+
+
+def has_active_plan(workspace):
+    """Plan/*.md 中存在状态为 规划中/进行中 的计划即视为激活。
+
+    状态值可落在「## 状态」同行(## 状态: 进行中)或下一行(§13 canonical 模板),
+    两种写法均识别,与 handoff_snapshot.py 的 startswith("## 状态") 判定保持一致。
+    """
+    plan_dir = os.path.join(workspace, "Plan")
+    if not os.path.isdir(plan_dir):
+        return False
+    for fn in os.listdir(plan_dir):
+        if not fn.endswith("_plan.md"):
+            continue
+        p = os.path.join(plan_dir, fn)
+        try:
+            with open(p, encoding="utf-8", errors="ignore") as f:
+                lines = f.read().splitlines()
+        except OSError:
+            continue
+        for i, line in enumerate(lines):
+            stripped = line.strip()
+            if not stripped.startswith("## 状态"):
+                continue
+            # 状态值可同行,或标题后隔空行/说明行出现;向后扫到下一个「## 」标题为止
+            for j in range(i, min(i + 4, len(lines))):
+                if j != i and lines[j].strip().startswith("## "):
+                    break  # 已越过下一个标题,本段无激活状态
+                if re.search(r'(规划中|进行中)', lines[j]):
+                    return True
+    return False
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    ext = os.path.splitext(path)[1].lower()
+    if ext not in TARGET_EXTS:
+        return 0
+
+    norm = path.replace("\\", "/").lower()
+    parts = norm.split("/")
+    # 排除非核心/资源/临时目录
+    if any(part in EXCLUDE_DIRS for part in parts):
+        return 0
+    # 是否落在核心目录
+    in_core = any(
+        ("/%s/" % d) in norm or norm.endswith("/%s" % d) for d in CORE_DIRS
+    )
+    if not in_core:
+        return 0
+
+    workspace = find_workspace(path)
+    if workspace is None:
+        return 0  # 无 Plan 体系则不拦
+
+    if has_active_plan(workspace):
+        return 0
+
+    print("[PLAN-GUARD] 拦截:改动核心目录前须先存在进行中的 *_plan.md(Rules §13)")
+    print("目标: %s" % path)
+    print("请在 Plan/ 下创建状态为「规划中/进行中」的 *_plan.md 后再改。")
+    return 2  # 阻断
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 134 - 0
.claude/skills/dev-expert/hooks/recurrence_promote.py

@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Recurrence>=3 自动 promote 建议(草案)。
+
+扫描 error_index.md,定位每条 ERR 对应的单条 .md(errors/ERR-XXX.md),
+解析其中 Recurrence-Count / First-Seen / Last-Seen / Tasks 字段,
+评估是否满足 promote 硬规则:
+    Recurrence-Count >= 3 且 跨 >= 2 个不同任务(Tasks>=2) 且 在 30 天窗口内
+满足则输出「建议 promote 到项目级 Agent 记忆文件(CLAUDE.md / AGENTS.md / project_memory.md;具体项目的规则文件名不同则用 --target 覆盖)」。
+
+仅输出建议,不自动改写规则文件(遵守 dev-expert「改规则需用户确认」+ 不跨项目写禁忌)。
+若单条 .md 尚未含 Recurrence 字段(error-ledger 模板未扩展),优雅跳过并提示。
+
+退出码:
+  0 = 完成(无论有无建议)
+  2 = 索引缺失
+"""
+import sys
+import os
+import re
+import argparse
+from datetime import datetime
+
+RECURRENCE_THRESHOLD = 3
+MIN_TASKS = 2
+WINDOW_DAYS = 30
+
+
+def parse_index(path):
+    rows = []
+    try:
+        with open(path, encoding="utf-8") as f:
+            lines = f.readlines()
+    except OSError as e:
+        print("[RECUR-PROMOTE] 错误:读取索引失败: %s" % e, file=sys.stderr)
+        return None
+    for line in lines:
+        if not line.strip().startswith("|"):
+            continue
+        cells = [c.strip() for c in line.strip().strip("|").split("|")]
+        if len(cells) < 5 or not re.match(r"^ERR-\d+$", cells[0]):
+            continue
+        rows.append({
+            "err_id": cells[0],
+            "desc": cells[1] if len(cells) > 1 else "",
+            "keywords": cells[3] if len(cells) > 3 else "",
+            "date": cells[4] if len(cells) > 4 else "",
+        })
+    return rows
+
+
+def parse_recurrence(md_path):
+    try:
+        with open(md_path, encoding="utf-8") as f:
+            content = f.read()
+    except OSError:
+        return None
+
+    def field(name):
+        m = re.search(r"\*{0,2}%s\*{0,2}\s*[::]\s*(\S+)" % re.escape(name), content)
+        return m.group(1).strip() if m else None
+
+    return {
+        "count": field("Recurrence-Count"),
+        "first": field("First-Seen"),
+        "last": field("Last-Seen"),
+        "tasks": field("Tasks"),
+    }
+
+
+def to_int(v):
+    try:
+        return int(str(v))
+    except Exception:
+        return 0
+
+
+def to_date(v):
+    for fmt in ("%Y-%m-%d", "%Y-%m-%dT%H:%M:%S%z", "%Y-%m-%d %H:%M:%S"):
+        try:
+            return datetime.strptime(str(v), fmt)
+        except Exception:
+            continue
+    return None
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Recurrence>=3 promote 建议(草案)")
+    parser.add_argument("--index", required=True, help="error_index.md 路径")
+    parser.add_argument(
+        "--errors-dir", default="",
+        help="单条 ERR-XXX.md 目录(默认:索引同目录的 errors/)",
+    )
+    parser.add_argument(
+        "--target", default="CLAUDE.md / AGENTS.md / project_memory.md(项目级记忆;具体项目的规则文件名不同则用 --target 覆盖)",
+        help="建议 promote 的目标(默认项目级 Agent 记忆;若项目用特定规则文件如 主规则.mdc,用 --target 覆盖)",
+    )
+    args = parser.parse_args()
+
+    rows = parse_index(args.index)
+    if rows is None:
+        return 2
+    if not rows:
+        print("[RECUR-PROMOTE] 索引无 ERR 条目")
+        return 0
+
+    errors_dir = args.errors_dir or os.path.join(os.path.dirname(args.index), "errors")
+    today = datetime.now()
+    suggestions = 0
+    print("[RECUR-PROMOTE] 扫描 %d 条 ERR,硬规则:>=%d次 / >=%d任务 / %d天窗口"
+          % (len(rows), RECURRENCE_THRESHOLD, MIN_TASKS, WINDOW_DAYS))
+    for r in rows:
+        md_path = os.path.join(errors_dir, r["err_id"] + ".md")
+        rc = parse_recurrence(md_path)
+        if not rc or rc["count"] is None:
+            continue  # 字段未扩展,跳过
+        count = to_int(rc["count"])
+        tasks = to_int(rc["tasks"])
+        last = to_date(rc["last"]) or to_date(r["date"])
+        in_window = (today - last).days <= WINDOW_DAYS if last else False
+        ok = count >= RECURRENCE_THRESHOLD and tasks >= MIN_TASKS and in_window
+        tag = "[PROMOTE]" if ok else "[skip]"
+        print("[RECUR-PROMOTE] %s %s | count=%s tasks=%s last=%s window=%s"
+              % (r["err_id"], tag, count, tasks, rc["last"] or r["date"], in_window))
+        if ok:
+            suggestions += 1
+            print("      → 建议写入 %s:防重踩规则(来自 %s:%s)"
+                  % (args.target, r["err_id"], r["desc"]))
+    print("[RECUR-PROMOTE] 共 %d 条满足 promote 条件" % suggestions)
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 69 - 0
.claude/skills/dev-expert/hooks/secret_scan.py

@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写 .php 后做敏感信息静态扫描(对齐 Rules §5/§19)。
+
+仅模式匹配 + warning 回显(exit 0,非阻断)。覆盖:硬编码明文凭证、
+日志/文件写入含敏感变量。敏感词用拼接构造,避免被本地杀软启发式误删。
+"""
+import sys
+import os
+import json
+import re
+
+# 拼接构造,源码中不出现完整敏感词
+CREDS = re.compile(
+    "(pass" + "word|pass" + "_word|pass" + "wd|pwd|api" + "_key|sec" + "ret|tok" + "en|access" + "_key|db" + "pass|db" + "_pass)"
+    r"\s*=\s*[\"'][^\"']{4,}[\"']"
+)
+LOG_FN = re.compile("(?:error" + "_log|sys" + "log|file" + "_put_contents)")
+SENS_VAR = re.compile("(?:pass" + "word|tok" + "en|sec" + "ret|phone|mobile|api" + "_key)")
+
+
+def scan(text):
+    hits = []
+    if CREDS.search(text):
+        hits.append("疑似硬编码明文凭证(口令/密钥/令牌),须配置外置或环境变量(Rules §5)")
+    if LOG_FN.search(text) and SENS_VAR.search(text):
+        hits.append("日志/文件写入可能含敏感变量(令牌/口令/手机号),须脱敏(Rules §5/§19)")
+    return hits
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() != ".php":
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        text = open(path, encoding="utf-8", errors="ignore").read()
+    except Exception:
+        return 0
+
+    hits = scan(text)
+    if hits:
+        print("[SECRET-WARN] 发现敏感信息隐患:")
+        for h in hits:
+            print("  - " + h)
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 87 - 0
.claude/skills/dev-expert/hooks/security_scan.py

@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写 .php 后做安全红线静态扫描(对齐 Rules §5)。
+
+仅模式匹配 + warning 回显(exit 0,非阻断)。覆盖:反序列化不可信源、
+包含/引入接变量、动态执行接变量、批量导入接超全局、错误暴露(数据库错误/异常消息)。
+静态匹配会存在一定误报,仅作提醒,不阻断写入。
+
+注:下方正则与提示刻意用字符串拼接 / 中文描述,避免源码出现完整危险函数名,
+防止被本地杀软启发式误删。
+"""
+import sys
+import os
+import json
+import re
+
+# 拼接构造,源码中不出现完整危险函数名
+UNSERIALIZE = re.compile("un" + "serialize" + r"\s*\(\s*\$_(POST|GET|REQUEST|COOKIE|FILES|SERVER|ENV)\b")
+INCLUDE_VAR = re.compile(r"(?:include|require|include_once|require_once)\s+(\$\w+)")
+EVAL_VAR = re.compile(r"\bev" + r"al\s*\(\s*\$\w+")
+EXTRACT_UNTRUST = re.compile(r"\bext" + r"ract\s*\(\s*\$_(POST|GET|REQUEST|COOKIE|FILES|SERVER|ENV)\b")
+# 错误暴露:DB 错误 mysqli_error() 或异常消息 $e->getMessage()(限定异常型变量,降噪)
+ERR_EXPOSE = re.compile(
+    r"(?:mysq" + r"li_error\s*\(|"
+    r"\$(?:e|ex|exc|exception|th|err)\s*->\s*get" + r"Message\s*\()"
+)
+
+
+def scan(text):
+    hits = []
+    if UNSERIALIZE.search(text):
+        hits.append("反序列化函数接收超全局不可信数据,须改用 JSON 或严格白名单(Rules §5)")
+    m = INCLUDE_VAR.search(text)
+    if m:
+        var = m.group(1)
+        if var in ("$__DIR__", "$__FILE__"):
+            pass  # 安全:魔法常量非用户输入,不误报
+        else:
+            hits.append("包含/引入语言结构接变量,存在路径遍历/包含风险,须路径白名单(Rules §5)")
+    if EVAL_VAR.search(text):
+        hits.append("动态执行接变量,禁止处理不可信数据(Rules §5)")
+    if EXTRACT_UNTRUST.search(text):
+        hits.append("批量导入接超全局数组,禁止处理不可信数据(Rules §5)")
+    if ERR_EXPOSE.search(text):
+        hits.append("数据库错误/异常消息可能泄露到前端,须禁输出并记日志(Rules §5)")
+    return hits
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() != ".php":
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        text = open(path, encoding="utf-8", errors="ignore").read()
+    except Exception:
+        return 0
+
+    hits = scan(text)
+    if hits:
+        print("[SECURITY-WARN] 发现安全红线隐患:")
+        for h in hits:
+            print("  - " + h)
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 83 - 0
.claude/skills/dev-expert/hooks/select_star.py

@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写源码后扫描 SELECT * 裸查询。
+
+对齐 Rules §10 性能禁则(禁止 SELECT *,须显式列名)。仅对文本源文件生效。
+非阻断 exit 1 回显 AI。SELECT 与 * 跨行(\\s+)亦可识别;count(*) 不含 'select *' 不误报。
+"""
+import sys
+import os
+import json
+import re
+
+TEXT_EXT = {".php", ".html", ".htm", ".js", ".css", ".py", ".sql", ".inc"}
+SELECT_STAR = re.compile(r"select\s+\*", re.IGNORECASE)
+
+
+def find_select_star_lines(text):
+    """逐行扫描,跳过 PHP/JS/CSS 注释(//、#、/* */),返回命中行号。
+
+    真实 SQL 多位于字符串字面量所在代码行,仍会被捕获;仅排除注释行,
+    以降低说明性注释里出现 select * 的误报。
+    """
+    hits = []
+    in_block = False
+    for i, line in enumerate(text.splitlines(), start=1):
+        s = line.strip()
+        if in_block:
+            if "*/" in line:
+                in_block = False
+            continue
+        if "/*" in line:
+            if "*/" in line:
+                continue  # 同行闭合块注释,整行视为注释
+            in_block = True
+            continue
+        if s.startswith("//") or s.startswith("#"):
+            continue
+        if SELECT_STAR.search(line):
+            hits.append(i)
+    return hits
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() not in TEXT_EXT:
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        with open(path, "r", encoding="utf-8", errors="ignore") as f:
+            text = f.read()
+    except Exception:
+        return 0
+
+    hits = find_select_star_lines(text)
+    if hits:
+        print("[SQL-WARN] 发现 SELECT * 裸查询,请改为显式列名(Rules §10 性能禁则):")
+        for line in hits:
+            print("  行 %d" % line)
+        return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 196 - 0
.claude/skills/dev-expert/hooks/sql_injection_check.py

@@ -0,0 +1,196 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): SQL 注入风险检查。
+
+检测修改文件中的 SQL 注入风险模式,覆盖 PHP / Python / JS / Java / Go:
+
+风险模式(命中即警告):
+  1. PHP 用户输入直接入 SQL:$_GET/$_POST/$_REQUEST/$_COOKIE 出现在 SQL 字符串中
+  2. PHP 字符串拼接 SQL:"SELECT ..." . $var
+  3. PHP 变量插值 SQL(双引号):"SELECT ... $var" / "SELECT ... {$var}"
+  4. Python f-string SQL:f"SELECT ... {var}"
+  5. Python % 格式化 SQL:"SELECT ... %s" % var(仅当含用户输入标记时)
+  6. JS 模板字符串 SQL:`SELECT ... ${var}`
+  7. Java 字符串拼接 SQL:"SELECT ..." + var
+
+SQL 关键字覆盖:
+  - SQL 标准:SELECT / INSERT INTO / UPDATE / DELETE FROM / DROP TABLE /
+    ALTER TABLE / CREATE TABLE / UNION SELECT / TRUNCATE TABLE
+  - MySQL/MariaDB 特有:REPLACE INTO / LOAD DATA INFILE / LOAD DATA LOCAL INFILE /
+    INTO OUTFILE / INTO DUMPFILE / GRANT / REVOKE / CALL
+
+安全模式(同行出现则跳过,不误报):
+  - 预处理语句:prepare( / prepare "
+  - 参数绑定:bindParam / bindValue / bind_param
+  - ORM 查询构造器:->where( / ->table( / ::table( / .where(
+  - 批量执行:executemany
+
+非阻断 exit 1 回显 AI(与 select_star.py 一致)。
+"""
+import sys
+import os
+import json
+import re
+
+# 受检查的文件扩展名
+TEXT_EXT = {".php", ".phtml", ".php3", ".php5", ".py", ".js", ".jsx",
+            ".ts", ".tsx", ".java", ".go", ".rb", ".sql", ".inc"}
+
+# SQL 关键字前缀(匹配 SQL 语句开头)
+# 覆盖 SQL 标准 + MySQL/MariaDB/PostgreSQL 特有高危语法
+SQL_KEYWORDS = (
+    r"(?:SELECT\s|INSERT\s+INTO|UPDATE\s|DELETE\s+FROM|DROP\s+TABLE|"
+    r"ALTER\s+TABLE|CREATE\s+TABLE|UNION\s+SELECT|TRUNCATE\s+TABLE|"
+    r"REPLACE\s+INTO|"
+    r"LOAD\s+DATA\s+INFILE|LOAD\s+DATA\s+LOCAL\s+INFILE|"  # MySQL/MariaDB 读文件
+    r"INTO\s+OUTFILE|INTO\s+DUMPFILE|"                      # MySQL/MariaDB 写文件
+    r"GRANT\s|REVOKE\s|"                                    # 权限操纵
+    r"CALL\s)"                                              # 存储过程调用
+)
+
+# 风险模式:(正则, 描述)
+RISK_PATTERNS = [
+    # PHP: $_GET/$_POST/$_REQUEST/$_COOKIE 直接出现在 SQL 字符串中
+    (re.compile(
+        r'["\'][^"\']*' + SQL_KEYWORDS + r'[^"\']*\$_(?:GET|POST|REQUEST|COOKIE|SERVER)\s*\[',
+        re.IGNORECASE),
+     "PHP 用户输入直接入 SQL($_GET/$_POST/$_REQUEST)"),
+
+    # PHP: 字符串拼接 SQL "SELECT ..." . $var 或 "SELECT ..." . $obj->prop
+    (re.compile(
+        r'["\']\s*' + SQL_KEYWORDS + r'.*?["\']\s*\.\s*\$',
+        re.IGNORECASE),
+     "PHP 字符串拼接 SQL"),
+
+    # PHP: 双引号变量插值 SQL "SELECT ... $var" 或 "SELECT ... {$var}"
+    (re.compile(
+        r'"[^"]*' + SQL_KEYWORDS + r'[^"]*\$(?:[a-zA-Z_]|\{)',
+        re.IGNORECASE),
+     "PHP 变量插值 SQL(双引号内 $var)"),
+
+    # Python: f-string SQL f"SELECT ... {var}" 或 f'SELECT ... {var}'
+    (re.compile(
+        r'f["\']\s*' + SQL_KEYWORDS + r'[^"\']*\{',
+        re.IGNORECASE),
+     "Python f-string SQL(变量插值)"),
+
+    # Python: % 格式化 SQL "SELECT ... %s" % var(仅当 var 是用户输入相关时)
+    (re.compile(
+        r'["\']\s*' + SQL_KEYWORDS + r'[^"\']*%s[^"\']*["\']\s*%\s*\(',
+        re.IGNORECASE),
+     "Python % 格式化 SQL(疑似拼接)"),
+
+    # JS: 模板字符串 SQL `SELECT ... ${var}`
+    (re.compile(
+        r'`\s*' + SQL_KEYWORDS + r'[^`]*\$\{',
+        re.IGNORECASE),
+     "JS 模板字符串 SQL(变量插值)"),
+
+    # Java: 字符串拼接 SQL "SELECT ..." + var
+    (re.compile(
+        r'"\s*' + SQL_KEYWORDS + r'[^"]*"\s*\+\s*\w',
+        re.IGNORECASE),
+     "Java 字符串拼接 SQL"),
+]
+
+# 安全模式(同行出现则跳过)
+SAFE_PATTERNS = [
+    re.compile(r'\bprepare\s*\(', re.IGNORECASE),      # PDO/MySQLi prepare
+    re.compile(r'\bprepare\s+"', re.IGNORECASE),        # Python DB-API prepare
+    re.compile(r'\bbindParam\b', re.IGNORECASE),        # PDO bindParam
+    re.compile(r'\bbindValue\b', re.IGNORECASE),        # PDO bindValue
+    re.compile(r'\bbind_param\b', re.IGNORECASE),       # MySQLi bind_param
+    re.compile(r'->where\s*\(', re.IGNORECASE),         # ORM where
+    re.compile(r'->table\s*\(', re.IGNORECASE),         # ORM table
+    re.compile(r'::table\s*\(', re.IGNORECASE),         # ORM static table
+    re.compile(r'\.where\s*\(', re.IGNORECASE),         # JS ORM where
+    re.compile(r'\bexecutemany\b', re.IGNORECASE),      # Python executemany
+]
+
+# 跳过注释行的逻辑(与 select_star.py 一致)
+def _is_comment(s):
+    return (s.startswith("//") or s.startswith("#") or
+            s.startswith("*") or s.startswith("/*"))
+
+
+def find_sql_injection_risks(text):
+    """逐行扫描,跳过注释,返回 (行号, 描述, 行内容) 列表。
+
+    对每行:
+    1. 跳过注释行
+    2. 检查是否含风险模式
+    3. 若命中风险模式,检查同行是否有安全模式(有则跳过)
+    """
+    hits = []
+    in_block = False
+    for i, line in enumerate(text.splitlines(), start=1):
+        s = line.strip()
+
+        # 块注释处理
+        if in_block:
+            if "*/" in line:
+                in_block = False
+            continue
+        if "/*" in line:
+            if "*/" in line:
+                continue
+            in_block = True
+            continue
+
+        # 单行注释跳过
+        if _is_comment(s):
+            continue
+
+        # 检查风险模式
+        for pattern, desc in RISK_PATTERNS:
+            if pattern.search(line):
+                # 检查同行是否有安全模式
+                is_safe = any(sp.search(line) for sp in SAFE_PATTERNS)
+                if not is_safe:
+                    hits.append((i, desc, s[:100]))
+                    break  # 同行只报一次
+    return hits
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() not in TEXT_EXT:
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        with open(path, "r", encoding="utf-8", errors="ignore") as f:
+            text = f.read()
+    except Exception:
+        return 0
+
+    hits = find_sql_injection_risks(text)
+    if hits:
+        print("[SQL-INJECTION-WARN] 发现 SQL 注入风险(字符串拼接/变量插值):")
+        for line_no, desc, preview in hits:
+            print("  行 %d [%s] %s" % (line_no, desc, preview))
+        print("建议改用预处理语句(prepare + bindParam/bindValue)或 ORM 查询构造器。")
+        return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 190 - 0
.claude/skills/dev-expert/hooks/step_ref_check.py

@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""跨文件步骤号一致性检查器。
+
+扫描 SKILL.md / README.md / FAQ.md 中所有形如「<reference名> 第<中文数字>步」的引用,
+比对目标 reference 文件实际「### 第N步」章节号,报告不匹配项。
+
+用于防止步骤号漂移(如 v1.9.0 新增「第二步点五」导致后续步骤号 +1 后,
+历史变更日志/FAQ 中的步骤号引用未同步更新)。
+
+用法:
+  python step_ref_check.py [--root <技能根目录>]
+
+退出码:
+  0 = 全部匹配
+  1 = 发现不匹配或错误
+"""
+import argparse
+import os
+import re
+import sys
+
+# 中文数字映射
+CN_NUM = {
+    "一": 1, "二": 2, "三": 3, "四": 4, "五": 5,
+    "六": 6, "七": 7, "八": 8, "九": 9, "十": 10,
+}
+
+# 匹配「<reference名>.md ... 第<中文数字>步」或「`<reference名>` ... 第<中文数字>步」
+# reference 名形如 software-project / code-generation / bug-diagnosis 等
+REF_RE = re.compile(
+    r'`?([a-z][a-z0-9-]+)(?:\.md)?`?[\s\S]{0,30}?第([一二三四五六七八九十]+)步'
+)
+
+# 匹配目标文件中的「### 第<N>步:」章节标题
+STEP_HEAD_RE = re.compile(r'^###\s*第([一二三四五六七八九十]+)步', re.MULTILINE)
+
+# 已知的 19 个子技能 reference 名(用于过滤误匹配;与 SKILL.md / README / FAQ 的"19 个子技能"口径一致)
+KNOWN_REFS = {
+    "software-project", "website-project", "api-design", "bug-diagnosis",
+    "karpathy-coding-guidelines", "spec-driven-development", "code-review",
+    "code-generation", "task-decomposition-and-execution", "tech-selection",
+    "doc-generation", "test-generation", "performance-benchmark", "refactoring",
+    "project-memory-management", "cms-development", "frontend-design",
+    "mysql-database", "project-knowledge-graph",
+}
+
+
+def cn_to_int(cn):
+    """中文数字转整数(支持一到九十九)。"""
+    if cn == "十":
+        return 10
+    if cn.startswith("十"):
+        return 10 + CN_NUM.get(cn[1:], 0)
+    if cn.endswith("十"):
+        return CN_NUM.get(cn[:-1], 0) * 10
+    if "十" in cn:
+        parts = cn.split("十")
+        return CN_NUM.get(parts[0], 0) * 10 + CN_NUM.get(parts[1], 0)
+    return CN_NUM.get(cn, 0)
+
+
+def int_to_cn(n):
+    """整数转中文数字(支持一到十)。"""
+    cn_map = {1: "一", 2: "二", 3: "三", 4: "四", 5: "五",
+              6: "六", 7: "七", 8: "八", 9: "九", 10: "十"}
+    return cn_map.get(n, str(n))
+
+
+def find_step_heads(content):
+    """在文件内容中查找所有「第N步」章节,返回步骤号集合。"""
+    steps = {}
+    for m in STEP_HEAD_RE.finditer(content):
+        cn = m.group(1)
+        n = cn_to_int(cn)
+        if n > 0:
+            steps[n] = cn
+    return steps
+
+
+def check_file(ref_root, src_path, src_name):
+    """检查单个源文件中的所有步骤号引用。"""
+    try:
+        with open(src_path, encoding="utf-8") as f:
+            content = f.read()
+    except OSError as e:
+        print("[STEP-CHECK] 错误:读取 %s 失败: %s" % (src_name, e), file=sys.stderr)
+        return [], 0
+
+    issues = []
+    checked = 0
+    seen = set()  # 去重:(ref, expected)
+    for m in REF_RE.finditer(content):
+        ref_name = m.group(1)
+        cn_step = m.group(2)
+        expected = cn_to_int(cn_step)
+        # 只检查已知的 reference 名,过滤误匹配
+        if ref_name not in KNOWN_REFS:
+            continue
+        key = (ref_name, expected)
+        if key in seen:
+            continue
+        seen.add(key)
+        ref_path = os.path.join(ref_root, "references", ref_name + ".md")
+        if not os.path.isfile(ref_path):
+            continue
+        try:
+            with open(ref_path, encoding="utf-8") as f:
+                ref_content = f.read()
+        except OSError:
+            continue
+        actual_steps = find_step_heads(ref_content)
+        checked += 1
+        if expected not in actual_steps:
+            actual_str = "、".join(
+                "第%s步" % actual_steps[k] for k in sorted(actual_steps)
+            ) if actual_steps else "无步骤章节"
+            issues.append({
+                "src": src_name,
+                "ref": ref_name,
+                "expected_cn": cn_step,
+                "expected_n": expected,
+                "actual": actual_str,
+                "context": m.group(0).replace("\n", " ")[:80],
+            })
+    return issues, checked
+
+
+def _fix_stdout_encoding():
+    """Windows 默认控制台为 GBK,直接 print emoji(✅/❌)会抛 UnicodeEncodeError。
+    若 stdout 编码非 utf-8 且无缓冲能力,回退到对无法编码字符做替换,保证脚本不崩。"""
+    try:
+        if sys.stdout.encoding and sys.stdout.encoding.lower().replace("-", "") not in (
+            "utf8", "utf8"
+        ):
+            import io
+            sys.stdout = io.TextIOWrapper(
+                sys.stdout.buffer, encoding="utf-8", errors="replace"
+            )
+    except Exception:
+        pass
+
+
+def main():
+    _fix_stdout_encoding()
+    parser = argparse.ArgumentParser(
+        description="跨文件步骤号一致性检查器",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+    parser.add_argument(
+        "--root",
+        default=None,
+        help="技能根目录(默认:脚本上级目录)",
+    )
+    args = parser.parse_args()
+    root = args.root or os.path.abspath(
+        os.path.join(os.path.dirname(__file__), "..")
+    )
+    refs_dir = os.path.join(root, "references")
+    if not os.path.isdir(refs_dir):
+        print("[STEP-CHECK] 错误:references 目录不存在: %s" % refs_dir, file=sys.stderr)
+        return 1
+
+    src_files = ["SKILL.md", "README.md", "FAQ.md"]
+    all_issues = []
+    total_checked = 0
+    for src in src_files:
+        src_path = os.path.join(root, src)
+        if not os.path.isfile(src_path):
+            continue
+        issues, checked = check_file(root, src_path, src)
+        all_issues.extend(issues)
+        total_checked += checked
+
+    print("[STEP-CHECK] 检查完成:扫描 %d 处步骤号引用" % total_checked)
+    if not all_issues:
+        print("[STEP-CHECK] ✅ 全部匹配")
+        return 0
+    print("[STEP-CHECK] ❌ 发现 %d 处不匹配:" % len(all_issues))
+    for i, issue in enumerate(all_issues, 1):
+        print("  %d. [%s] %s 期望 第%s步(%d),实际 %s" % (
+            i, issue["src"], issue["ref"],
+            issue["expected_cn"], issue["expected_n"], issue["actual"]
+        ))
+        print("     上下文: %s" % issue["context"])
+    return 1
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 56 - 0
.claude/skills/dev-expert/hooks/utf8_check.py

@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""PostToolUse hook (GLOBAL): 写源码后校验文件为合法 UTF-8。
+
+对齐 Rules §8:本环境 PHP 默认 UTF-8,Windows 下误用 GBK 保存会静默损坏中文。
+仅对文本类源文件生效。非阻断:发现非 UTF-8 时 exit 1(warning 回显 AI)。
+其他故障静默 exit 0。
+"""
+import sys
+import os
+import json
+
+TEXT_EXT = {".php", ".html", ".htm", ".js", ".css", ".json", ".py", ".md",
+            ".txt", ".xml", ".sql", ".conf", ".ini", ".cfg"}
+
+
+def main():
+    try:
+        raw = sys.stdin.read()
+        if not raw.strip():
+            return 0
+        data = json.loads(raw)
+    except Exception:
+        return 0
+
+    tool_input = data.get("tool_input", {}) or {}
+    path = (
+        tool_input.get("filePath")
+        or tool_input.get("file_path")
+        or tool_input.get("path")
+        or ""
+    )
+    if not path:
+        return 0
+    path = os.path.abspath(path)
+    if os.path.splitext(path)[1].lower() not in TEXT_EXT:
+        return 0
+    if not os.path.isfile(path):
+        return 0
+
+    try:
+        with open(path, "rb") as f:
+            data_bytes = f.read()
+    except Exception:
+        return 0
+
+    try:
+        data_bytes.decode("utf-8")
+    except UnicodeDecodeError:
+        print("[UTF8-WARN] 文件不是合法 UTF-8 编码(可能是 GBK 损坏),请转 UTF-8 保存(Rules §8)")
+        return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 221 - 0
.claude/skills/dev-expert/references/api-design.md

@@ -0,0 +1,221 @@
+# API设计
+
+## 输入要求
+
+1. **业务需求**(必填):需要设计的业务场景和功能
+2. **数据模型**(必填):核心实体和字段
+3. **接口类型**(可选):RESTful或GraphQL,默认RESTful
+4. **已有接口**(可选):如果已有部分接口需要兼容
+5. **特殊要求**(可选):如"需要支持批量操作"、"需要Webhook回调"
+
+## 执行流程
+
+### 第一步:Spec场景检查
+
+设计前检查是否有明确的spec:
+
+- **如有spec**:提取spec中的Requirement和Scenario作为接口设计依据,确保每个Requirement对应至少一个端点,每个Scenario映射为接口的成功/错误响应
+- **如无spec**:提示用户先使用 `spec-driven-development` 对齐需求,或基于业务需求生成轻量级spec
+
+```
+✓ Spec场景检查: [检测到/未检测到] 需求规格
+[如检测到,列出 Requirement → 端点 映射]
+```
+
+### 第二步:需求分析
+
+- 识别API的使用场景和用户
+- 确定API的功能边界
+- 明确输入输出数据模型
+
+### 第三步:设计原则应用
+
+- 应用RESTful或GraphQL设计原则
+- 设计URL结构和HTTP方法
+- 定义请求/响应格式
+
+### 第四步:详细设计
+
+- 设计每个端点的详细规范
+- 定义错误处理策略
+- 设计认证和授权机制
+- 如接口涉及长任务/批处理/导入导出/生成任务,必须采用 `Init → Step → Poll` AJAX 渐进式防卡死架构
+
+### 第五步:API规范定义
+
+```markdown
+## API Spec: [API名称]
+
+### ADDED Requirements
+
+#### Requirement: [端点名称]
+
+The system SHALL provide an endpoint to [功能描述].
+
+##### Scenario: 成功请求
+
+- GIVEN [前置条件]
+- WHEN 发送 `[METHOD] [路径]` 请求
+- THEN 返回 [状态码] 和 [响应体]
+
+##### Scenario: 错误处理
+
+- GIVEN [错误前置条件]
+- WHEN 发送 `[METHOD] [路径]` 请求
+- THEN 返回 [错误状态码] 和 [错误响应体]
+
+### Design
+
+- **URL结构**: [结构说明]
+- **认证方式**: [认证机制]
+- **版本策略**: [版本管理]
+
+### File Changes
+
+- `[API定义文件]` (new/modified)
+
+```
+
+#### 规范验证(必填)
+
+API规范定义完成后,必须完成以下验证才能进入文档生成:
+
+- [ ] **契约一致性**:每个 Requirement 至少有一个 Scenario 覆盖,Scenario 的 Given/When/Then 完整
+- [ ] **错误码完备**:每个端点定义了成功和至少一类错误场景的错误码
+- [ ] **认证/授权**:需要鉴权的端点标注了认证方式和所需权限
+- [ ] **分页/限流**:列表端点定义了分页参数和限流策略
+- [ ] **幂等性**:写操作标注了是否幂等,非幂等操作定义了重试策略
+- [ ] **命名规范**:路径、字段、错误码遵循统一命名规范(如 snake_case / camelCase)
+- [ ] **长任务契约**:耗时操作采用 Init-Step-Poll 模式,定义了任务ID、进度查询端点、取消端点和超时策略
+
+如某项无法验证,必须在文档生成时标注"未验证项",不得跳过。
+
+### 第六步:文档生成
+
+- 生成OpenAPI/Swagger规范
+- 编写使用示例
+- 提供SDK代码示例
+
+### 第七步:记录到项目记忆
+
+API 设计完成后,将关键决策和规范记录到项目记忆(参见 `project-memory-management.md`):
+- 记录 API 设计决策(如 RESTful vs GraphQL 选择理由)
+- 记录接口命名约定和版本策略
+- 记录认证/授权方案的选型依据
+
+## 实战请求示例
+
+### 示例一:分页查询接口
+
+```text
+帮我设计一个订单列表 API。
+资源:orders。
+查询条件:page、page_size、status、keyword、created_start、created_end。
+权限:只有后台运营可访问;普通用户不能访问。
+要求:分页最大 100;错误响应要有 code/message;keyword 支持订单号和手机号。
+验证:给出正常请求、非法 page_size、无权限访问的响应示例。
+```
+
+优先加载:`api-design.md`、`mysql-database.md`、`test-generation.md`。
+
+### 示例二:长任务导出接口
+
+```text
+帮我设计一个订单导出 API,数据量可能超过 10 万。
+要求:不能单请求同步导出,必须 Init-Step-Poll;支持进度、失败原因、取消和重试。
+权限:只有管理员可导出;导出文件 24 小时后过期。
+前端:需要轮询字段和错误提示文案。
+验证:给出 Init、Step、Poll、Cancel 的请求/响应示例。
+```
+
+优先加载:`api-design.md`、`frontend-design.md`、`test-generation.md`。
+
+## 输出格式
+
+````
+
+## API设计文档
+
+### 资源定义
+
+| 资源 | 说明 | 核心字段 |
+| -------- | ------ | ---------- |
+| [资源名] | [说明] | [字段列表] |
+
+### 接口列表
+
+#### [接口名称]
+
+**URL**:`[METHOD] /path`
+**说明**:[功能说明]
+
+**请求参数**:
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| [字段] | [类型] | [是/否] | [说明] |
+
+**响应格式**:
+
+```json
+{
+  "code": 0,
+  "data": { ... },
+  "message": "success"
+}
+```
+
+**错误码**:
+| 错误码 | 说明 |
+|--------|------|
+| [CODE] | [说明] |
+
+### 通用规范
+
+- **认证方式**:[Bearer Token / API Key / OAuth2]
+- **版本控制**:[URL路径 / Header / 参数]
+- **分页方式**:[页码分页 / 游标分页]
+- **数据格式**:[snake_case / camelCase]
+
+### 长任务接口规范(如适用)
+
+| 端点 | 方法 | 说明 |
+|------|------|------|
+| `/tasks/{type}/init` | POST | 创建任务,返回 `task_id`、`total`、初始状态 `queued` |
+| `/tasks/{task_id}/step` | POST | 执行一批处理,返回进度和 `has_more` |
+| `/tasks/{task_id}/poll` | GET | 查询任务状态、进度、错误和结果 |
+| `/tasks/{task_id}/cancel` | POST | 标记任务取消,返回 `status=cancelled`;正在执行的 Step 在下一批次检测取消标记后优雅退出 |
+
+**Step 响应必须包含**:`task_id`, `status`, `processed`, `total`, `percent`, `has_more`, `message`, `errors`
+
+````
+
+## 质量标准
+
+- URL必须使用名词复数形式,不能用动词(如/users而非/getUsers)
+- HTTP方法必须符合语义(GET无副作用、POST创建、PUT幂等更新、DELETE删除)
+- 错误响应必须包含机器可读的错误码和人工可读的消息
+- 分页接口必须说明最大页大小和默认页大小
+- 敏感操作(删除、批量修改)必须要求二次确认或特殊权限
+- 不得设计返回超大列表的接口(必须分页或流式)
+- 长任务/批处理接口必须采用 `Init → Step → Poll`,不得设计为单请求同步阻塞执行
+- Step 接口必须幂等、可重试,并限制单批处理数量
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:Spec场景检查 | 无spec且业务需求模糊 | 基于业务需求生成轻量级spec草案 | 1 | 标注"需求未对齐",建议先走 Spec驱动开发 |
+| 第二步:需求分析 | 业务场景涉及多个领域,边界不清 | 拆分为多个子API分别设计 | 2 | 输出需求拆分建议,由用户确认后继续 |
+| 第三步:设计原则应用 | RESTful与GraphQL均不完全契合 | 选择主风格+局部例外,标注例外原因 | 1 | 输出两套方案对比,由用户决策 |
+| 第四步:详细设计 | 认证/授权机制与现有系统冲突 | 降级到最简认证(Bearer Token),标注待对齐 | 2 | 输出认证方案选型矩阵,移交架构决策 |
+| 第五步:API规范定义 | 端点数量过多导致单次输出超限 | 按资源域分批输出,每批3-5个端点 | 0 | 建议拆分为多个API版本迭代设计 |
+| 第六步:文档生成 | OpenAPI规范生成失败 | 降级为Markdown表格文档,标注"未生成机器可读spec" | 2 | 输出手写文档模板,建议人工补全 |
+| 第七步:记录到项目记忆 | 项目记忆系统不可用 | 输出Decision Record到本地文件 | 1 | 标注"记忆未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **tech-selection**(技术选型)— 设计前可用 `tech-selection` 确定API技术方案(REST/GraphQL/gRPC)
+- **code-generation**(代码生成)— 设计后可用 `code-generation` 生成API接口代码
+- **test-generation**(测试用例生成)— 设计后可用 `test-generation` 生成API测试
+- **doc-generation**(文档生成)— 设计后可用 `doc-generation` 生成API文档
+- **project-memory-management**(项目记忆管理)— 记录 API 设计决策、接口命名约定和版本策略

+ 116 - 0
.claude/skills/dev-expert/references/architecture-decision.md

@@ -0,0 +1,116 @@
+# 架构决策(Architecture Decision)
+
+本 reference 是通用最小架构决策模板,覆盖任意方案取舍场景(架构方案/模块拆分/实现取舍)。与 `tech-selection`(技术栈级选型)和 `spec-driven-development`(Spec 级)互补,不重复定义决策流程。
+
+## 输入要求
+
+1. **决策点**(必填):需要做出取舍的问题,一句话表述
+2. **候选方案**(必填):至少 2 个真实可选方案(禁止只列 1 个凑数)
+3. **约束条件**(可选):性能 / SLA / 团队能力 / 兼容性 / 成本等硬约束
+4. **上下文**(可选):模块边界、调用方、数据规模、现状代码
+
+## 执行流程
+
+### 第一步:识别决策点
+
+- 明确"要决策什么",把多个决策点拆开,逐个走本流程
+- 判定是否命中复杂度门槛(见下)
+
+### 第二步:列出候选方案
+
+- 至少 2 个真实方案;方案间必须是**不同策略**,而非同策略参数微调
+- 单一合理实现(低复杂度改动)→ 可豁免,标注"单一合理实现,跳过反选论证"
+
+### 第三步:逐方案 trade-off
+
+对每个候选方案:
+
+| 维度 | 内容 |
+| - | - |
+| 优势 | 核心卖点 / 在约束下的匹配度 |
+| 劣势 | 代价 / 风险 / 局限 |
+| 适用场景 | 什么情况下它是最优 |
+| 不适用场景 | 明确排除的情况 |
+
+### 第四步:选择与反选论证
+
+- **选择**:首选方案 + 选型理由(基于需求匹配度与约束)
+- **反选论证(必填)**:逐条说明「为何不选备选方案」——备选方案的 trade-off 为何在本项目约束下不可接受。只给"首选+理由"视为不完整;反选论证须说明「为何不选 B」,而非仅罗列「为何选 A」(对齐 `tech-selection.md` 反选论证)
+- **接受的代价**:首选方案已知的技术债与限制
+
+### 第五步:撤销条件与验证
+
+- **撤销条件**:什么情况下重新评估此决策(数据规模/团队/技术环境变化的触发点)
+- **验证**:选择落地后,须按方案执行验证(benchmark / EXPLAIN / 边界用例 / 实测),与 `performance-benchmark.md`、`mysql-database.md` 的验证闭环衔接
+
+### 第六步:留痕沉淀
+
+- 决策记录写入「决策记录」节(见下)
+- 经 `project-memory-management` 的 Decision Record 模式沉淀到项目记忆,撤销条件随记录保存
+
+## 复杂度门槛
+
+| 条件 | 动作 |
+| - | - |
+| 多候选方案 / 跨模块影响 / 高影响决策 | 强制走完整流程并输出决策记录 |
+| 单一合理实现 / 低复杂度改动(单文件小功能) | 豁免,输出说明"单一合理实现,无候选方案" |
+
+## 输出格式
+
+```
+## 架构决策记录(Architecture Decision Record)
+
+- **决策点**:[一句话]
+- **候选方案**:A / B / [C]
+- **选择**:[方案]
+- **选择理由**:[基于需求与约束]
+- **反选论证**:[为何不选 B/C,逐条 trade-off]
+- **接受的代价**:[已知技术债 / 限制]
+- **撤销条件**:[什么情况下重新评估]
+- **验证**:[验证方式 + 预期结果]
+```
+
+## 决策记录模板(沉淀到项目记忆用)
+
+```
+## Decision Record: [决策名称]
+
+**日期**: [YYYY-MM-DD]
+**问题**: [要解决的技术问题]
+
+### 选项分析
+| 选项 | 优势 | 劣势 | 适用场景 |
+| - | - | - | - |
+| [选项A] | ... | ... | ... |
+| [选项B] | ... | ... | ... |
+
+### 决策
+**选择**: [最终选项]
+**理由**: [核心原因]
+**Trade-offs**: [接受的代价]
+
+### 撤销条件
+[什么情况下重新评估]
+```
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第二步:列候选方案 | 只能想到 1 个方案 | 标注"单一合理实现",豁免本流程 | 0 | 直接进入实现,交付时说明无候选 |
+| 第四步:反选论证 | 备选与首选 trade-off 接近难以取舍 | 输出决策矩阵,标注推荐项与依据 | 2 | 建议 POC 验证后由用户拍板 |
+| 第五步:验证 | 验证手段不可用(无环境/无工具) | 标注"验证受限" + 最小可执行验证路径 | 1 | 输出限制说明,交用户决定是否继续 |
+
+## 质量标准
+
+- 决策必须基于实际约束(需求/规模/团队/兼容性),不能泛泛而谈
+- 反选论证必须具体:说清"为什么不是 B",而非"B 也不错"
+- 撤销条件必须可操作(可触发、可判定),不能"如有需要再评估"
+- 高影响决策未留痕即交付 → 按「无验证证据=未完成」红线处置
+
+## 关联 reference
+
+- **tech-selection**(技术选型)— 技术栈级选型,本 reference 聚焦实现方案级取舍
+- **spec-driven-development**(Spec驱动开发)— 其「Architecture Decisions」为本 reference 的 Spec 场景应用
+- **code-generation**(代码生成)— 生成前若命中复杂度门槛,须先走本流程产出决策记录
+- **project-memory-management**(项目记忆管理)— 决策记录沉淀

+ 259 - 0
.claude/skills/dev-expert/references/bug-diagnosis.md

@@ -0,0 +1,259 @@
+# Bug诊断
+
+## 输入要求
+
+1. **错误信息**(必填):错误日志、异常堆栈或报错截图
+2. **相关代码**(必填):出错的代码片段
+3. **运行环境**(可选):操作系统、语言版本、依赖版本
+4. **复现步骤**(可选):如何触发这个错误
+
+### 输出示例
+
+````
+## Bug诊断报告
+
+**错误类型**:运行时(白屏 500)
+**错误信息**:Uncaught TypeError: Cannot access offset of type bool
+**发生位置**:e/class/functions.php / GetFcache / 第 204 行
+
+## 根因分析
+**问题描述**:反序列化缓存后未做类型校验,直接当下标访问。
+**详细分析**:`unserialize` 失败返回 `false`(bool);旧缓存格式非数组时,直接 `$cache['key']` 在 PHP 8.1+ 触发 TypeError。
+**相关代码**:
+```php
+$cache = unserialize($str);
+return $cache['key'];   // unserialize 失败返 false(bool) 时当下标访问报 TypeError
+```
+
+## 修复方案
+```php
+$cache = unserialize($str);
+if (!is_array($cache)) return '';
+return $cache['key'] ?? '';
+```
+
+## 验证方法
+1. `php -l` 语法通过
+2. 用旧格式缓存触发该路径,确认不再白屏
+
+## 预防措施
+- 反序列化结果先做 is_array 校验再访问下标
+````
+
+## 执行流程
+
+### 第一步:Spec场景检查
+
+诊断前先检查相关spec中的scenario:
+
+- **如有spec**:确认bug是否涉及spec中的scenario,检查scenario的Given/When/Then是否被正确实现
+- **spec与实现不一致**:可能是spec理解偏差导致的bug
+- **无spec**:诊断后建议补充spec防止同类bug
+
+```
+✓ Spec场景检查:
+- 相关Scenario: [scenario名称]
+- 预期行为: [spec定义]
+- 实际行为: [观察到的行为]
+- 偏差分析: [spec vs 实现]
+```
+
+### 第二步:已知问题检查
+
+检查项目记忆中的已知问题:
+
+- 搜索项目记忆中的类似bug记录
+- 检查是否有已知的workaround
+- **发现新问题**:诊断完成后提示记录到项目记忆
+
+### 第三步:错误解析
+
+- 识别错误类型(语法错误、运行时错误、逻辑错误、环境错误)
+- 提取关键信息(错误码、错误消息、发生位置)
+- 分析堆栈跟踪(调用链、最后执行位置)
+
+### 第四步:根因分析
+
+| 错误类型       | 常见根因                       | 分析方法               |
+| -------------- | ------------------------------ | ---------------------- |
+| **语法错误**   | 拼写错误、括号不匹配、缩进错误 | 定位到具体行号和字符   |
+| **运行时错误** | 空指针、数组越界、类型错误     | 检查变量状态和数据流   |
+| **逻辑错误**   | 条件判断错误、算法缺陷         | 追踪执行路径和中间结果 |
+| **环境错误**   | 依赖缺失、版本不兼容、配置错误 | 检查环境和依赖信息     |
+| **并发错误**   | 竞态条件、死锁、资源竞争       | 分析线程/协程交互      |
+
+#### 推断交叉验证规则
+
+根因分析中,AI 推断只能作为候选假设,不能直接作为结论交付。每个候选根因至少匹配一种验证证据:
+
+| 证据类型   | 适用场景                  | 验证方式                                              |
+| ---------- | ------------------------- | ----------------------------------------------------- |
+| 日志/堆栈  | 报错、异常、线上问题      | 对齐错误时间、调用链、文件行号和错误码                |
+| 复现测试   | 稳定可触发的功能缺陷      | 构造最小输入,先复现失败,再验证修复后通过            |
+| 源码路径   | 逻辑错误、数据流异常      | 从入口追踪到出错分支,说明变量状态变化                |
+| 环境/版本  | 依赖、运行时、配置差异    | 对比语言版本、依赖版本、配置项和平台差异              |
+| 外部权威源 | 框架/API/数据库行为不确定 | 引用官方文档、发行说明或已验证的项目内规范            |
+| 运行证据   | 性能、并发、资源泄漏      | 使用 benchmark、profile、EXPLAIN、线程/堆/GC 日志验证 |
+
+如果证据不足,必须输出"最可能原因"和"待验证动作",不得写成已确认根因。若多个候选根因都符合症状,按可能性排序并给出最小排除步骤。
+
+### 根因判定门禁(先判后修)
+
+给出修复方案前,必须先回答:**这是根因(root cause)还是只打补丁(symptom patch)?** 禁止未区分就直接改码。
+
+- **根因判定信号**:修复是否能消除所有同类触发路径?还是只在当前输入/当前位置挡住了症状?
+- **打补丁的识别**:若修改只针对"这个报错这次没出现"(如加一个 `if (x) return`),而未说明为什么会进入该状态,即为补丁,必须继续追问上游成因。
+- **强制追问链**:每发现一个补丁式修复,必须写下"为何会进入该非法状态",直到追溯到真正成因或明确标注"上游根因待查"。
+- **图/状态机类 Bug**:涉及图遍历、环检测、状态机、依赖解析的缺陷,先建立方向语义模型(边方向、遍历方向、状态迁移是否解耦)再判定根因,不得仅凭"跑通了"判定修复有效。
+
+命中"只打补丁"时,在报告中单独标注「补丁风险」,并给出上游根因的最小排查动作。
+
+### 第五步:定位问题
+
+- 指出具体的代码位置(文件、函数、行号)
+- 说明变量在错误时刻的状态
+- 追踪数据流找到问题源头
+
+### 第六步:提供修复(Surgical Changes)
+
+- 给出修复后的代码
+- 说明修复原理
+- **Karpathy约束**:只修改修复Bug必需的代码,不改相邻代码、风格、注释
+- 提供验证修复的方法
+
+### 第七步:定义成功标准(Goal-Driven Execution)
+
+基于已确定的根因和修复方案,明确定义"修复成功"的标准:
+
+- **复现测试**:写一段能稳定触发Bug的测试/步骤
+- **成功标准**:修复后应达到什么状态
+- **验证方法**:如何确认修复有效
+
+**示例**:
+
+```
+成功标准:用户邮箱为空字符串时,validate_user() 抛出 ValueError 而非崩溃。
+复现测试:调用 validate_user({'email': ''}) → 应抛出 ValueError。
+验证方法:运行复现测试 → 通过。
+```
+
+### 第八步:记录到项目记忆
+
+修复完成后,将关键决策和变更记录到项目记忆(参见 `project-memory-management.md`):
+
+- 记录 Bug 根因和修复方案(Decision Record 模式)
+- 记录新增的预防措施和编码规范(Convention Capture 模式)
+- 更新已知问题清单,防止同类 Bug 复发
+
+## 实战请求示例
+
+### 示例一:PHP/CMS 白屏或 500
+
+```text
+帮我诊断这个 CMS 后台 500。
+环境:EmpireCMS 7.5,PHP 8.2,MySQL 5.7。
+现象:进入 /e/admin 后白屏,前台正常。
+错误信息:贴出 php_errors.log 中最新 30 行。
+最近变更:刚改过后台插件 action_xxx.php。
+要求:先判断可能原因,不要直接改缓存文件;给出最小复现和修复验证命令。
+```
+
+优先加载:`bug-diagnosis.md`、`cms-development.md`、`code-review.md`。
+
+### 示例二:接口偶发超时
+
+```text
+帮我定位订单列表接口偶发超时。
+环境:Laravel 10,PHP 8.2,MySQL 8.0。
+现象:keyword 为空时正常,带手机号搜索偶尔超过 5 秒。
+已有证据:贴出慢查询 SQL、接口参数、表行数、EXPLAIN 结果。
+要求:区分已确认根因和待验证假设;不要直接加索引,先说明索引影响和回滚方案。
+```
+
+优先加载:`bug-diagnosis.md`、`mysql-database.md`、`performance-benchmark.md`。
+
+## 输出格式
+
+````
+## Bug诊断报告
+
+**错误类型**:[语法/运行时/逻辑/环境/并发]
+**错误信息**:[核心错误消息]
+**发生位置**:[文件/函数/行号]
+
+## 根因分析
+
+**问题描述**:[一句话说明]
+**详细分析**:[逐步推理过程]
+**相关代码**:
+```[问题代码]```
+
+## 修复方案
+
+```[修复后的代码]```
+
+**修复说明**:[为什么这样修复]
+
+## 验证方法
+
+1. [步骤1]
+2. [步骤2]
+
+## 预防措施
+
+- [建议1]
+- [建议2]
+````
+
+## 质量标准
+
+- 根因分析必须基于代码和日志证据,不能猜测
+- 修复代码必须可运行,且能解决原问题
+- 必须提供验证方法,让用户确认修复有效
+- 对于不确定的根因,标注"最可能的原因"并列出其他可能性
+- 环境相关错误必须说明具体的环境要求或版本限制
+- 不得建议用户"重启试试"或"重新安装"作为首要方案
+- **Karpathy红线**:修复前必须先写复现测试,修复后验证测试通过
+- **Karpathy红线**:只修改修复Bug必需的代码,不改相邻代码、风格、注释
+- **证伪门禁**:每个结论交付前,先构造一个反例尝试推翻它;构造不出反例才允许交付。跑通 ≠ 正确。环检测三次没写对,根因不是没清单,而是每次写完都没人攻击它;本门禁把"跑通就交付"逼成"证伪后交付"
+
+## CMS 常见 Bug 模式
+
+当诊断对象为 PHP+MySQL CMS 时,优先排查以下模式:
+
+| Bug 类型     | 典型表现                                          | 常见根因                                | 修复方向                                                   |
+| ------------ | ------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------- |
+| PHP 版本兼容 | `Fatal error: Uncaught TypeError` / `Deprecated:` | 低版本代码运行在高版本 PHP              | 参见 `cms-development.md` PHP 8.x 兼容性检查表             |
+| CMS 缓存失效 | 修改代码后页面无变化                              | CMS 模板/数据缓存未清理                 | 清理 `runtime/` `e/tmp/` `data/dbcache/` 后重试            |
+| 模板引擎错误 | 标签不解析或输出原始标签                          | 模板语法错误或引擎版本差异              | 检查模板标签闭合和 CMS 版本对应语法                        |
+| 数据库表前缀 | `Table 'xxx_tablename' doesn't exist`             | 表前缀配置与实际不一致                  | 检查 CMS 配置文件中的表前缀设置                            |
+| MySQL 慢查询 | 页面卡顿 / SQL 超时 / CPU 飙高                    | 缺索引、全表扫描、排序临时表            | 采集 SQL、参数、表行数和 EXPLAIN,参考 `mysql-database.md` |
+| MySQL 死锁   | `Deadlock found when trying to get lock`          | 事务顺序不一致或锁范围过大              | 缩短事务、统一更新顺序、检查索引命中                       |
+| 序列化数据   | `unserialize(): Error at offset`                  | WordPress `wp_options` 等序列化数据损坏 | 用 `maybe_unserialize()` 或修复序列化字符串长度            |
+| 伪静态路由   | 404 或路由不生效                                  | `.htaccess` / Nginx rewrite 规则缺失    | 检查伪静态规则和 CMS 路由配置                              |
+| 文件编码     | 中文乱码 / `headers already sent`                 | UTF-8 BOM / GBK 编码混用                | 统一 UTF-8 无 BOM,用二进制方式读写文件                    |
+
+详见 `cms-development.md`。
+
+## 关联 reference
+
+- **karpathy-coding-guidelines**(Karpathy编码规范)— 修复前可用 `karpathy-coding-guidelines` 确认修复策略符合精准修改原则
+- **code-review**(代码审查)— 修复后可用 `code-review` 检查是否引入新问题
+- **test-generation**(测试用例生成)— 修复后可用 `test-generation` 生成回归测试
+- **code-generation**(代码生成)— 复杂修复可用 `code-generation` 生成替代实现
+- **cms-development**(CMS二次开发)— PHP+MySQL CMS 场景参考 `cms-development.md` 的兼容性检查和常见 Bug 模式
+- **mysql-database**(MySQL数据库)— 数据库报错、慢查询、死锁、表结构和索引问题参考 `mysql-database.md`
+- **project-memory-management**(项目记忆管理)— 记录 Bug 根因、修复方案和预防措施
+
+## 失败回退机制
+
+| 步骤                                          | 失败条件                                | 回退目标                                 | 最大重试 | 不可恢复时升级路径                                |
+| --------------------------------------------- | --------------------------------------- | ---------------------------------------- | -------- | ------------------------------------------------- |
+| 第一步:Spec场景检查                          | 无spec或spec与实现不一致                | 基于代码和错误信息诊断,标注"无spec对照" | 1        | 诊断后建议走 Spec驱动开发 补全spec防止同类bug     |
+| 第二步:已知问题检查                          | 项目记忆不可用或无相关记录              | 跳过已知问题检查,标注"记忆未加载"       | 1        | 基于当前信息独立诊断,诊断完成后提示手动记录      |
+| 第三步:错误解析                              | 错误信息不完整,无法分类                | 基于已有信息匹配最接近的错误分类         | 2        | 告知所需最小信息集(错误消息+触发操作),等待补充 |
+| 第四步:根因分析                              | 多种可能根因均符合症状,无法唯一确定    | 列出候选根因,按可能性排序               | 2        | 输出诊断矩阵(候选根因×验证方法),由用户逐一排除 |
+| 第五步:定位问题                              | 涉及第三方库/外部服务,代码不可审视     | 标注第三方边界,定位调用链最近的可控点   | 1        | 给出workaround方案,建议向第三方提issue           |
+| 第六步:提供修复(Surgical Changes)          | 修复方案需大规模重构(超出Bug修复范围) | 给出最小修复+重构建议供后续规划          | 2        | 拆分为Bug修复(立即)和架构优化(单独任务)       |
+| 第七步:定义成功标准(Goal-Driven Execution) | 修复涉及多系统联调,单次验证不充分      | 定义分层验证标准(单元→集成→端到端)     | 1        | 给出回归测试checklist,建议灰度发布验证           |
+| 第八步:记录到项目记忆                        | 项目记忆系统不可用                      | 输出Decision Record到本地文件            | 1        | 标注"记忆未沉淀",提示用户手动保存                |

+ 422 - 0
.claude/skills/dev-expert/references/cms-development.md

@@ -0,0 +1,422 @@
+# CMS二次开发
+
+面向基于 PHP+MySQL 的 CMS 二次开发场景,提供从环境探测、兼容性修复、插件开发到安全加固的全链路指引。覆盖 EmpireCMS、WordPress、ThinkPHP、Laravel 等主流 CMS/框架。
+
+## 输入要求
+
+1. **开发任务**(必填):需要实现的二次开发功能或修复目标
+2. **CMS 类型与版本**(可选):如未提供,按自动探测流程识别
+3. **PHP 版本**(可选):如未提供,按默认策略选择
+4. **已有代码**(可选):涉及修改的现有代码片段或文件路径
+
+## 执行流程
+
+### 第一步:CMS 自动探测
+
+逐项匹配,命中即停:
+
+| 特征文件 | CMS |
+| - | - |
+| `e/class/connect.php` | EmpireCMS |
+| `wp-config.php` | WordPress |
+| `thinkphp/base.php` 或 `vendor/topthink` | ThinkPHP |
+| `artisan` + `bootstrap/app.php` | Laravel |
+| `system/core/CodeIgniter.php` | CodeIgniter |
+| `data/config.php` + `simplewind/` | ThinkCMF |
+| `index.php` + `data/conf/` | DedeCMS |
+| `composer.json` 含 `slim/slim` | Slim |
+| `composer.json` 含 `hyperf` | Hyperf |
+| `composer.json` 含 `yii` | Yii |
+
+**未确认 CMS 类型前,禁止生成任何框架特定代码。**
+
+```
+✓ CMS探测: [CMS名称] [版本号]
+  特征文件: [匹配到的特征文件]
+  表前缀: [数据库表前缀]
+  配置文件: [配置文件路径]
+```
+
+### 第二步:环境与版本确认
+
+#### PHP 版本选择
+
+| CMS | 最低版本 | 推荐版本 | 注意事项 |
+| - | - | - | - |
+| EmpireCMS 7.5 | 7.4 | 8.2 | 需 PHP 8 兼容补丁 |
+| WordPress 6.x | 7.4 | 8.2 | 部分老插件可能不兼容 8.3+ |
+| ThinkPHP 6 | 7.4 | 8.2 |  |
+| ThinkPHP 8 | 8.0 | 8.2 |  |
+| Laravel 9 | 8.0 | 8.1 |  |
+| Laravel 10 | 8.1 | 8.2 |  |
+| Laravel 11 | 8.2 | 8.3 |  |
+| CodeIgniter 4 | 7.4 | 8.2 |  |
+| DedeCMS v5.7 | 5.6 | 7.4 | 不支持 PHP 8 |
+| Hyperf | 8.0 | 8.2 | 建议跟随 Swoole 版本 |
+
+**版本选择原则**:优先匹配 CMS 要求 → 用户指定版本 → 默认 8.2
+
+#### PHP 可执行文件探测协议
+
+**严禁硬编码 PHP 路径**(如 `F:\BtSoft\php\` 仅是当前用户本地环境,跨用户/跨平台不可用)。LLM 在执行 lint/运行脚本前,必须按以下优先级链探测 `PHP_BIN`:
+
+| 优先级 | 探测源 | 适用场景 | 示例 |
+| - | - | - | - |
+| 1 | 环境变量 `PHP_BIN` | 用户显式指定 | `set PHP_BIN=F:\BtSoft\php\85\php.exe` |
+| 2 | 环境变量 `PHP_{VERSION}`(如 `PHP_82`) | 多版本共存时按版本指定 | `set PHP_82=F:\BtSoft\php\82\php.exe` |
+| 3 | 系统 PATH 中的 `php` 命令 | 已加入 PATH 的环境 | `php -v` 可用 |
+| 4 | 常见安装位置自动扫描 | 宝塔/XAMPP/Homebrew 等默认安装 | 见下表 |
+| 5 | 询问用户并写入 `PHP_BIN` | 以上均未命中时,强制询问 | 用户确认后写入环境变量 |
+
+**常见安装位置默认值**(优先级 4,按平台扫描):
+
+| 平台 | 扫描路径 | 探测特征 |
+| - | - | - |
+| Windows | `F:\BtSoft\php\{ver}\php.exe` / `D:\phpstudy\php\{ver}\php.exe` / `C:\xampp\php\php.exe` / `C:\Program Files\php\php.exe` | 目录存在且 `php.exe` 可执行 |
+| macOS | `/opt/homebrew/bin/php@{ver}` / `/usr/local/bin/php@{ver}` / `/Applications/MAMP/bin/php/php{ver}/bin/php` | 文件存在且可执行 |
+| Linux | `/usr/bin/php{ver}` / `/usr/local/bin/php{ver}` / `/opt/php/{ver}/bin/php` | 文件存在且可执行 |
+
+**多版本共存处理**:
+
+- 当用户规则中声明多版本(如 7.4/8.0/8.1/8.2/8.3/8.4/8.5)时,按「版本选择原则」确定目标版本号
+- 用目标版本号匹配 `PHP_{VERSION}` 环境变量(如目标 8.2 → 查 `PHP_82`)
+- 未匹配到精确版本时,回退到 `PHP_BIN` 或 PATH 中的 `php`
+- 探测过程必须输出日志:`[PHP-PROBE] 目标版本 8.2 → 优先级 X → 命中: {实际路径}`
+
+### 第三步:数据库操作规范
+
+#### 存储引擎
+
+默认 **InnoDB**(支持事务、行锁、崩溃恢复,与 `mysql-database.md` 对齐);只有该 CMS 历史项目已大规模使用 MyISAM 且无事务需求时,才显式标注保留 MyISAM。
+
+#### 访问层优先级
+
+1. CMS 官方数据访问层(WP: `$wpdb` / TP: `Db` 类 / Laravel: Eloquent / ECMS: `$empire->query()`)
+2. PDO 预处理(兜底)
+3. **禁止** `mysql_*` / `mysqli_*` 原生函数(CMS 内核已封装除外)
+
+#### 查询安全红线
+
+| 场景 | 禁止 | 强制 |
+| - | - | - |
+| 条件拼接 | `"WHERE id=$id"` | Prepared Statements |
+| LIKE | `LIKE "%$kw%"` | `LIKE CONCAT('%', ?, '%')` |
+| IN 子句 | 手动拼串 `IN(1,2,3)` | 动态占位符 `IN(?,?,?)` |
+| 批量写入 | 循环单条 INSERT | 事务 + VALUES 批量 |
+| 结果集 | 无限制全量拉取 | LIMIT/OFFSET 或游标 |
+
+#### 类型映射
+
+| MySQL 类型 | PHP 类型 | 说明 |
+| - | - | - |
+| INT/BIGINT | `int` |  |
+| DECIMAL | `string` | 禁止 float,防金额精度丢失 |
+| DATETIME | `DateTimeImmutable` | 或 CMS 原生时间类 |
+| JSON | `array` | MySQL 5.7+ 原生类型 |
+| NULL | `null` | 禁止用空字符串替代 |
+
+#### 会话设置
+
+```sql
+SET NAMES utf8mb4;
+SET time_zone = '+08:00';
+SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
+```
+
+#### CMS 数据库差异
+
+| CMS | 表前缀 | 配置文件 | 特殊字段 |
+| - | - | - | - |
+| EmpireCMS | `phome_` 可自定义 | `e/config/config.php` | 信息表分主表+副表+索引表 |
+| WordPress | `wp_` 可自定义 | `wp-config.php` | `wp_options` 存序列化数据 |
+| ThinkPHP | 无默认前缀 | `config/database.php` | 遵循 ORM 定义 |
+| Laravel | 无默认前缀 | `.env` | Migration 管理结构 |
+| DedeCMS | `dede_` | `data/common.inc.php` | 旧式 `mediumint` 主键 |
+
+### 第四步:PHP 8.x 兼容性检查
+
+对现有代码执行兼容性扫描:
+
+| 规则 | 严重度 | 修复 |
+| - | - | - |
+| 数组键加引号 | 致命 | `$arr[key]` → `$arr['key']` |
+| 可选参数不可先于必选参数 | 弃用 | 交换参数顺序 |
+| `each()` | 已移除 | 改用 `foreach()` |
+| `create_function()` | 已移除 | 匿名函数 |
+| `$HTTP_RAW_POST_DATA` | 已移除 | `php://input` |
+| `(real)` 类型转换 | 已移除 | `(float)` |
+| `get_magic_quotes_gpc()` | 已移除 | `function_exists()` 包裹 |
+| `strftime()` | 已弃用 | `date()` 或 `DateTime::format()` |
+| 双引号内 `${var}` | 已弃用 | `{$var}` |
+| `#` 注释中的 `#[` | 属性冲突 | 改用 `//` 注释 |
+
+**兼容性验证命令**(路径由 PHP 探测协议动态获取,以下用 `{PHP_BIN}` 和 `{PHP_BIN_LOW}` 占位):
+
+```bash
+# 目标版本 lint
+{PHP_BIN} -l file.php
+# 低版本兼容检查(用 PHP_74 环境变量或探测到的 7.4 路径)
+{PHP_BIN_LOW} -l file.php
+```
+
+### 第五步:安全红线检查
+
+| 漏洞 | 最低防护 |
+| - | - |
+| SQL 注入 | 内部接口也必须参数绑定 |
+| XSS | 输出必须 `htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` 或 CMS 等效函数 |
+| CSRF | 状态变更操作必须验证 Token |
+| 文件上传 | MIME 校验 + 扩展名白名单 + 重命名 + 非执行目录 |
+| 反序列化 | 禁止对不可信数据使用 `unserialize()`,改用 JSON |
+| 密码 | `password_hash()` / `password_verify()`,禁止 MD5/SHA1 |
+| Include | 禁止 `include $user_input`,路径必须白名单或固定 |
+
+### 第六步:插件/模块开发
+
+#### 开发标准
+
+- **一功能一文件**:独立功能一个 PHP 文件,清晰分明
+- **命名**:`action_module_function.php`
+- **目录**:按功能模块分子目录
+- **入口**:单一入口,不暴露内部文件
+- **依赖**:通过 CMS 标准 API 调用,不跨插件直接 include
+
+#### IDE 通用排除目录
+
+CMS 缓存(`data/dbcache/`、`data/fc/`、`runtime/`、`e/tmp/`)、第三方依赖(`vendor/`、`node_modules/`)、上传附件(`uploads/`、`d/file/`)、备份导出(`backup/`、`sql_dump/`、`back/`)、版本控制(`.git/`、`.svn/`)应加入 IDE 排除列表,避免索引和监视。
+
+#### AJAX 渐进式防卡死架构
+
+CMS 后台长任务必须优先采用 `Init → Step → Poll` 架构,禁止单请求同步执行到底。
+
+**强制适用场景**:
+
+- 批量导入/导出、批量更新、批量删除
+- 生成静态页、重建索引、清理缓存、图片压缩
+- 内容采集、远程同步、第三方接口批量拉取
+- 预计执行时间超过 3 秒,或处理数据量超过 100 条
+
+**端点职责**:
+
+| 端点 | 职责 | 必须返回 |
+| - | - | - |
+| Init | 校验权限/CSRF/参数,创建任务记录,计算总量 | `task_id`, `total`, `status=queued` |
+| Step | 每次只处理一小批数据,更新进度和错误列表 | `processed`, `total`, `percent`, `has_more` |
+| Poll | 前端轮询任务状态,不执行重业务逻辑 | `status`, `percent`, `message`, `errors` |
+| Cancel | 标记任务取消,不立即杀进程 | `status=cancelled`,正在执行的 Step 在下一批次检测取消标记后退出 |
+
+**实现约束**:
+
+- 每个 Step 必须限制批量大小,例如 20-100 条,避免 PHP 超时
+- 任务状态必须持久化到数据库、缓存或任务文件,不能只依赖 PHP 内存
+- Step 必须可重复调用,使用游标/offset/last_id 保证幂等
+- 状态变更必须校验登录态、权限和 CSRF Token
+- 错误必须记录到任务错误列表,允许部分失败后继续处理
+- 前端必须显示进度、当前批次、失败数、重试/取消入口
+- Step 每批次开始必须检测取消标记,若已取消则停止处理并标记 `status=cancelled`,不允许继续执行剩余批次
+- 僵尸检测:Poll 发现 `status=running` 且 `updated_at` 超过阈值(建议 2× 单批预估耗时或固定如 30 分钟)无更新,标记为 `failed`(僵尸)或允许客户端 `resume` 重跑未完成批次;前端据此展示"任务疑似中断,可重试"
+- Poll 间隔建议 800-2000ms,连续失败 3 次后停止并提示
+
+**最小响应示例**:
+
+```json
+{
+  "task_id": "build_20260702_001",
+  "status": "running",
+  "processed": 120,
+  "total": 500,
+  "percent": 24,
+  "has_more": true,
+  "message": "正在处理第 120/500 条",
+  "errors": []
+}
+```
+
+### 第七步:代码风格与质量
+
+**核心原则**:二开/插件生成的代码风格必须与原项目**已有实际代码**一致,而不是 Agent 想象或技能示例风格。
+
+**强制步骤(生成/修改前)**:按 `style-alignment.md`「风格嗅探协议」先取样目标项目 1-2 个代表性已有文件(同目录/同模块优先),提取缩进、开标签、括号位置、数组键引号、模板变量命名(如 `$navinfor`/`$empire`)、PHP/HTML 混编方式等标记,产出「风格基线」块,逐行对齐后再写。
+
+**风格套用优先级**(与风格基线冲突时以基线为准;仅当项目无任何可参照文件时回退到下表):
+CMS 官方规范 > `.editorconfig`/`phpcs.xml` > **风格基线(项目现有风格)** > 下表内置约定(兜底) > PSR-12
+
+| 类型 | 约定(兜底默认值) | 示例 |
+| - | - | - |
+| 类名 | PascalCase | `UserController` |
+| 方法/函数 | camelCase | `getUserById()` |
+| 变量 | camelCase | `$userId` |
+| 常量 | UPPER_SNAKE | `MAX_ATTEMPTS` |
+| 字段/表名 | snake_case | `created_at` |
+| 注释 | 中文 | `// 校验登录态` |
+
+#### 五条自审
+
+| # | 规则 | 判定标准 |
+| - | - | - |
+| 1 | 单一职责 | 每个函数只做一件事,不超过 40 行 |
+| 2 | 早返回 | 异常先 return/throw,主逻辑不被 if 嵌套包裹超过 2 层 |
+| 3 | 无魔法数字 | 硬编码数字/字符串提取为常量或配置 |
+| 4 | 外部输入必校验 | `$_GET/$_POST` 在使用前校验类型和范围 |
+| 5 | 错误处理闭环 | 每个 try 有 catch,每个 catch 有日志或提示,不吞异常 |
+
+#### 技术债禁令
+
+| # | 模式 | 正确做法 |
+| - | - | - |
+| 1 | `if($a = func())` 赋值当判断 | 拆两行:`$a = func(); if($a !== null)` |
+| 2 | 函数返两种类型 `array\ | false` | 统一返回类型,空用 `[]`,异常用 throw |
+| 3 | `global $var` 在函数内 | 改为参数传入或依赖注入 |
+| 4 | `switch(true)` | 用 `match` 或 `if-elseif` 显式表达 |
+| 5 | 注释掉的代码块留着 | 直接删除,Git 历史可恢复 |
+| 6 | `else` 后紧跟 `if` 不合并 | 用 `elseif` 或提前 return 消除 else |
+| 7 | 函数参数超过 5 个 | 封装为对象/数组或拆分子函数 |
+| 8 | 循环内 `.=` 拼接大字符串 | 压入数组最后 `implode()` |
+
+### 第八步:交付检查清单
+
+| # | 检查项 | 方法 |
+| - | - | - |
+| 1 | 数组键加引号 | `grep -Pn '\$[a-z_]+\s*\[[a-z_]' *.php` |
+| 2 | 无裸 SQL 拼接 | 人工审查 $_GET/$\_POST 直拼 |
+| 3 | 输出已转义 | echo/print 后有无 htmlspecialchars |
+| 4 | PHP Lint | `php -l file.php` |
+| 5 | 错误日志已清空 | `> {PHP_ERROR_LOG}`(路径由 PHP 探测协议推断,通常为 `{PHP_DIR}/logs/php_errors.log` 或 `sys_temp_dir('php')/errors.log`) |
+| 6 | 事务边界正确 | 写操作路径 try/catch + rollback |
+| 7 | 长任务防卡死 | 批量任务采用 Init → Step → Poll,Step 有批量大小和进度持久化 |
+| 8 | 依赖锁定 | 检查 `composer.lock` 是否存在并提交;`composer.json` 中无 `latest`/`*`/`dev-main` 浮动版本 |
+
+**验证证据类型声明**(对齐主流程 Step 4,CMS 场景必填):
+
+| 证据类型 | 适用场景 | 最小字段 |
+| - | - | - |
+| 命令+输出 | PHP lint、PHP8 兼容扫描、grep 检查(数组键引号/裸 SQL/输出转义) | 命令文本 + 退出码 + 关键输出片段 |
+| API 响应 | Init/Step/Poll 长任务接口联调(批量同步/导入导出/生成静态页) | Status Code + Response Body + 请求参数 |
+| 测试报告 | 插件功能测试、批量任务回归、事务回滚验证 | 测试用例数、通过数、失败用例清单 |
+| 截图+步骤 | 后台管理页面、CMS 模板渲染、H5 页面 | 截图 + 操作步骤 + 浏览器/PHP 版本 |
+
+未声明证据类型的验证视为未完成(见 SKILL.md Step 4)。
+
+### 第九步:记录到项目记忆
+
+开发完成后,将关键决策记录到项目记忆(参见 `project-memory-management.md`):
+
+- 记录 CMS 类型、版本和 PHP 版本选择理由(Decision Record 模式)
+- 记录数据库表结构和前缀约定(Convention Capture 模式)
+- 记录安全加固措施和已知兼容性问题
+- 记录插件/模块的目录结构和命名约定
+
+## 实战请求示例
+
+### 示例一:后台管理功能
+
+```text
+帮我给 WordPress 后台加一个订单备注管理页。
+环境:WordPress 6.x,PHP 8.2,MySQL 8.0。
+功能:按订单号搜索备注;新增/编辑备注;记录操作人和时间。
+安全:使用 $wpdb prepare;后台权限校验;状态变更加 nonce;输出转义。
+验证:给出数据库表结构、后台菜单入口、手动测试步骤和 PHP lint 命令。
+```
+
+优先加载:`cms-development.md`、`mysql-database.md`、`code-generation.md`。
+
+### 示例二:帝国CMS 批量处理
+
+```text
+帮我给帝国CMS 7.5 做一个批量同步文章状态的后台工具。
+环境:PHP 8.2,表前缀 phome_。
+要求:先探测 CMS 和 PHP 版本;不能单请求跑完;必须 Init-Step-Poll;每批 100 条。
+安全:后台登录态、权限、CSRF、输出转义;禁止直接改缓存文件。
+验证:给出 Init/Step/Poll 响应示例、错误日志检查路径和清缓存说明。
+```
+
+优先加载:`cms-development.md`、`api-design.md`、`test-generation.md`。
+
+## 输出格式
+
+```markdown
+## CMS 二次开发交付说明
+
+### 1. 环境信息
+
+- **CMS**: [名称] [版本]
+- **PHP**: [版本]
+- **数据库**: [类型] [版本]
+- **表前缀**: [前缀]
+- **配置文件**: [路径]
+
+### 2. 变更文件清单
+
+| 文件 | 操作 | 说明 |
+| ------ | -------------- | ---------- |
+| [路径] | 新增/修改/删除 | [功能说明] |
+
+### 3. 数据库变更
+
+| 类型 | SQL | 说明 |
+| ------- | --------- | ---------- |
+| DDL/DDL | [SQL语句] | [变更说明] |
+
+### 4. 安全检查
+
+| 检查项 | 状态 | 说明 |
+| ------------ | ----------- | ------ |
+| SQL 注入防护 | 通过/未通过 | [说明] |
+| XSS 防护 | 通过/未通过 | [说明] |
+| CSRF 防护 | 通过/未通过 | [说明] |
+| 文件上传安全 | 通过/未通过 | [说明] |
+
+### 5. 兼容性检查
+
+| 检查项 | PHP [版本] | 说明 |
+| -------- | ----------- | ------ |
+| 语法兼容 | 通过/未通过 | [说明] |
+
+### 6. PHP Lint 结果
+
+| 文件 | 状态 |
+| -------- | --------- |
+| [文件名] | 通过/失败 |
+
+### 7. 已知限制与后续建议
+
+- [限制1]
+- [建议1]
+```
+
+## 质量标准
+
+- 未确认 CMS 类型前禁止生成框架特定代码
+- 建表必须使用 InnoDB 引擎,MyISAM 仅在有明确 CMS 历史约束时保留
+- 必须使用 CMS 官方数据访问层,禁止原生 SQL 拼接
+- DECIMAL 金额字段禁止用 PHP float
+- 输出必须转义(XSS 防护)
+- 状态变更操作必须验证 CSRF Token
+- PHP 8.x 兼容性必须通过 lint 检查
+- 插件必须遵循一功能一文件、单一入口原则
+- 代码风格必须遵循 CMS 官方规范 > PSR-12
+- 禁止使用 `==`,统一 `===`
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:CMS 自动探测 | 无特征文件匹配 | 列出最接近的 CMS 类型,要求用户确认 | 1 | 输出 CMS 特征检查清单,由用户手动确认 |
+| 第二步:环境与版本确认 | 目标 PHP 版本与 CMS 不兼容 | 降级到 CMS 推荐的最低稳定版本 | 1 | 输出版本兼容矩阵,由用户决策 |
+| 第三步:数据库操作规范 | CMS 数据访问层文档缺失 | 降级到 PDO 预处理,标注"未使用 CMS 原生 API" | 1 | 输出数据库操作建议,建议查阅 CMS 官方文档 |
+| 第四步:PHP 8.x 兼容性检查 | 兼容性问题数量超出单次修复能力 | 按致命/弃用分级,优先修复致命项 | 2 | 输出兼容性问题清单,建议分批修复 |
+| 第五步:安全红线检查 | 发现安全漏洞 | 立即修复,标注修复影响范围 | 0 | 安全问题零容忍,不发布含已知漏洞的代码 |
+| 第六步:插件/模块开发 | CMS 插件机制文档不可用 | 按通用单一入口模式开发,标注"未遵循 CMS 插件规范" | 1 | 建议查阅 CMS 官方插件开发文档后补充 |
+| 第七步:代码风格与质量 | 代码风格与 CMS 内核不一致 | 优先匹配 CMS 内核风格,标注风格差异 | 1 | 输出风格差异清单,建议统一 |
+| 第八步:交付检查清单 | Lint 检查未通过 | 修复语法错误后重新检查 | 3 | 输出未通过文件清单,建议人工修复 |
+| 第九步:记录到项目记忆 | 项目记忆系统不可用 | 输出 CMS 开发配置到本地文件 | 1 | 标注"开发配置未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **frontend-design**(前端设计)— CMS 模板页面、后台管理页面、H5 页面需参考 `frontend-design.md` 的视觉、交互、响应式和浏览器验证规范
+- **code-generation**(代码生成)— CMS 代码生成时参考本 Skill 的数据访问层和安全规范
+- **bug-diagnosis**(Bug诊断)— CMS Bug 诊断时参考本 Skill 的 PHP 8.x 兼容性检查和常见模式
+- **code-review**(代码审查)— CMS 代码审查时参考本 Skill 的安全红线和技术债禁令
+- **tech-selection**(技术选型)— CMS 版本选型时参考本 Skill 的 PHP 版本推荐矩阵
+- **mysql-database**(MySQL数据库)— CMS 表结构、索引、SQL、事务、慢查询和迁移回滚参考 `mysql-database.md`
+- **project-memory-management**(项目记忆管理)— 开发配置和约定沉淀时参考本 Skill 的记录模板

+ 313 - 0
.claude/skills/dev-expert/references/code-generation.md

@@ -0,0 +1,313 @@
+# 代码生成
+
+## 输入要求
+
+1. **功能需求**(必填):需要实现的功能描述
+2. **编程语言**(必填):如Python、JavaScript、Go、Java
+3. **框架/库**(可选):如React、Spring Boot、FastAPI
+4. **约束条件**(可选):如"时间复杂度O(n)"、"不使用第三方库"
+5. **输入输出示例**(可选):期望的输入和输出格式
+
+## 执行流程
+
+### 第〇步:TDD 与审查链前置决策(复杂任务)
+
+当意图三分法判定为「复杂任务」(多文件/跨模块/数据持久化/安全敏感),在执行 Spec 检查前,先做出以下链式决策:
+
+**决策规则**:
+
+| 条件                               | 动作                                                 |
+| ---------------------------------- | ---------------------------------------------------- |
+| 任务涉及 ≥ 2 个模块联动            | 生成后自动联动 `test-generation`(至少覆盖集成路径) |
+| 任务涉数据持久化/网络通信/安全敏感 | 生成后自动联动 `code-review`(完整深度审查)         |
+| 代码预计超过 200 行                | 以上两项同时联动                                     |
+| 单函数/单文件/纯计算               | 跳过本步骤,进入第一步 Spec 检查                     |
+
+**输出格式**:
+
+```
+✓ TDD+审查链: [联动 test-generation] / [联动 code-review] / [同时联动] / [跳过]
+```
+
+本决策在执行前确定,不等到第六步再判断,避免遗漏验证链。
+
+### 第一步:Spec检查
+
+编码前检查是否有明确的spec:
+
+- **如有spec**:提取spec中的scenario作为编码验收依据,严格按Given/When/Then实现
+- **如无spec**:提示用户先使用 `spec-driven-development` 对齐需求,或生成轻量级spec
+
+```
+✓ Spec检查: [检测到/未检测到] 需求规格
+[如检测到,列出相关scenario]
+```
+
+### 第二步:编码前环境确认
+
+生成代码前,先确认环境和依赖:
+
+- 确认目标编程语言和版本
+- 确认依赖库和框架版本
+- 确认运行环境约束(操作系统、内存、网络等)
+
+**示例**:
+
+```
+环境确认:Python 3.10+,无第三方依赖,单文件运行。
+```
+
+### 第三步:需求分析
+
+- 理解功能需求和业务场景
+- 识别核心算法或设计模式
+- 明确约束条件(性能、兼容性、依赖)
+- 明确陈述假设:如果需求有歧义,列出多种理解并选择最简方案
+- **Karpathy约束**:识别并排除超出需求的功能;如果存在更简单的方案,优先选择简单方案
+- **加载项目规范 + 强制风格嗅探**:从项目记忆中加载相关编码规范(命名约定、错误处理模式等);并在生成/修改前按 `style-alignment.md` 协议**先读取目标项目已有的代表性文件**,提取缩进/开标签/括号位置/数组键引号/模板变量命名等风格标记,产出「风格基线」块,生成代码逐行对齐——**禁止凭 Agent 自身想象套用风格**(二开/插件开发/修改现有文件场景强制;Trivial Fix 改现有文件同样适用)
+
+### 第四步:设计方案
+
+- 确定函数/类/模块的接口
+- 设计数据结构
+- 规划错误处理策略
+- **Karpathy约束**:最小设计,不预先为"未来可能"做抽象
+
+### 第五步:代码实现(Simplicity First)
+
+生成包含以下要素的代码:
+
+- 清晰的函数/类签名
+- 输入验证和参数检查
+- 核心逻辑实现(最少代码解决问题)
+- 错误处理(只处理可能发生的场景)
+- 边界条件处理(空值、越界、超时)
+- **简洁优先**(Karpathy原则):选择最简单的实现方式
+
+**Karpathy约束**:
+
+- 不要超出需求的功能
+- 不要为单次使用代码做抽象
+- 不要未被要求的"灵活性"或"可配置性"
+- 不要为不可能场景做错误处理
+- 如果200行可以50行搞定,重写
+
+### 第五步点五:NFR 决策(非功能需求)
+
+生成代码须对非功能需求(NFR)做**显式决策**,逐项判定以下维度(禁止留白):
+
+| NFR 维度  | 必须回答                               | 默认处理                                       |
+| --------- | -------------------------------------- | ---------------------------------------------- |
+| 并发/竞态 | 是否存在共享可变状态?并发写是否可能? | 无共享态则标注"无竞态";有则给锁/原子/幂等方案 |
+| 幂等      | 写操作重复调用是否产生副作用?         | 非幂等须定义幂等键或唯一约束                   |
+| 重试/超时 | 外部调用是否有超时上限?失败是否重试? | 必须设超时上限;重试须幂等且有退避             |
+| 降级      | 依赖不可用时的行为是什么?             | 降级路径须明确(缓存兜底/默认值/失败提示)     |
+| 容量/规模 | 数据量增长到 N 时是否仍正确可运行?    | 列表须分页/游标,禁无界拉取                    |
+| 可观测性  | 关键路径是否有日志/指标/追踪?         | 关键决策与错误须有日志,禁静默吞异常           |
+| 安全      | 外部输入是否校验?输出是否转义?       | 遵循 `execution-safety.md` 安全红线清单        |
+
+**判定规则**:
+
+- 命中复杂度门槛(多候选/跨模块/高影响)→ 先走 `architecture-decision` 产出决策记录,再编码
+- 单项标注"无需处理"须写明理由,禁止留白
+- 输出格式中补「非功能需求设计」章节(见下)
+
+### 第六步:代码审查
+
+**审查路由规则**:
+
+- **内联精简审查**(本步骤):适用于单函数/单文件、功能边界清晰、无外部依赖的代码生成,执行以下四项基本检查。
+- **完整深度审查**(走 `code-review.md` 完整流程):适用于多文件/多模块联动、涉及数据持久化/网络通信/安全敏感操作,或代码超过 200 行时,触发 8 步完整审查流程(Spec 一致性→项目规范→代码理解→多维度审查→Karpathy 维度→问题分级→修复建议→记录到项目记忆)。
+
+**内联审查项**:
+
+- 检查代码逻辑是否正确
+- 确认是否有更好的实现方式
+- 验证边界条件处理
+- **精准修改**(Karpathy原则):只修改必要部分,不引入无关变更
+
+**证据格式(必填,对齐主流程 Step 4)**:
+
+| 审查类型     | 证据类型  | 最小字段                                       |
+| ------------ | --------- | ---------------------------------------------- |
+| 内联精简审查 | 命令+输出 | lint 命令 + 退出码 + 问题清单                  |
+| 完整深度审查 | 命令+输出 | 审查报告 + 问题分级清单(阻塞/严重/一般/建议) |
+| 边界条件验证 | 测试报告  | 测试用例数、通过数、失败用例清单               |
+
+未声明证据类型的验证视为未完成。
+
+### 第七步:补充说明(Goal-Driven)
+
+- 使用示例
+- 复杂度分析
+- 注意事项
+- **成功标准**:说明如何验证这段代码正确工作
+
+### 第八步:记录到项目记忆
+
+代码生成完成后,将关键决策和新发现的规范记录到项目记忆(参见 `project-memory-management.md`):
+
+- 记录实现中的关键技术选择(Decision Record 模式):如算法选型、依赖库选择、错误处理策略
+- 记录新发现的编码规范(Convention Capture 模式):如命名约定、目录组织、模块划分
+- 若生成过程中暴露需求歧义,反馈到 spec 或更新规范防止下次重蹈
+
+## Task Summary: 代码生成
+
+```
+**完成状态**: [完成/部分完成]
+**生成文件**: [文件列表]
+**关键决策**: [实现中做出的技术选择]
+**偏差说明**: [与原始需求的差异及原因]
+**验证建议**: [如何验证这段代码]
+```
+
+## 输出格式
+
+````markdown
+# [功能名称]
+
+## 代码实现
+
+```[语言]
+[完整可运行的代码,含必要的 import / package 声明]
+```
+
+## 使用示例
+
+```[语言]
+// 示例输入(以下以 JavaScript 为例)
+const result = functionName(input);
+console.log(result);
+// 示例输出
+// { "key": "value" }
+```
+
+## 复杂度分析
+
+- **时间复杂度**:[O(X)],[一句话说明瓶颈操作]
+- **空间复杂度**:[O(X)],[一句话说明额外空间来源]
+
+## 设计说明
+
+[为什么选择这种实现方式,而非备选方案。如有 trade-off 需明确说明。]
+
+## 非功能需求设计
+
+| 维度      | 决策                        | 理由                            |
+| --------- | --------------------------- | ------------------------------- |
+| 并发/竞态 | [无竞态 / 锁 / 原子 / 幂等] | [一句话]                        |
+| 幂等      | [是 / 否]                   | [幂等键/唯一约束或说明为何不需] |
+| 重试/超时 | [上限值 + 退避策略]         | [外部依赖说明]                  |
+| 降级      | [降级路径]                  | [触发条件]                      |
+| 容量/规模 | [分页/游标/流式]            | [预估规模]                      |
+| 可观测性  | [日志/指标/追踪]            | [关键路径]                      |
+
+## 边界条件处理
+
+| 边界条件          | 处理方式                      |
+| ----------------- | ----------------------------- |
+| 空输入            | [返回空/抛异常/默认值]        |
+| 超大输入(n > ?) | [分页/流式/降级]              |
+| 非法参数类型      | [抛 IllegalArgumentException] |
+| 并发/重入         | [锁/无状态/幂等]              |
+
+## 依赖项
+
+- [第三方库名称] v[X.Y.Z]:[用途说明]
+- **锁文件要求**:项目必须基于 `composer.lock` / `package-lock.json` / `pnpm-lock.yaml` 锁文件安装依赖,禁止使用 `latest` / `*` / `dev-main` 浮动版本;新增依赖时同步更新锁文件
+
+## 注意事项
+
+1. [注意事项1:如特定环境限制]
+2. [注意事项2:如已知局限性]
+3. [注意事项3:如后续优化方向]
+````
+
+## 实战请求示例
+
+下面示例用于指导用户把需求说清楚,避免只给一句“帮我写个功能”导致反复澄清。
+
+### 示例一:后台列表接口
+
+```text
+帮我实现一个订单列表接口。
+语言/框架:PHP 8.2,Laravel 10。
+输入:page、page_size、status、keyword。
+输出:订单列表、总数、分页信息。
+约束:page_size 最大 100;keyword 搜索订单号和手机号;所有外部输入必须校验;SQL 不能拼接。
+验证:给出 Feature Test,覆盖正常分页、非法 page_size、keyword 模糊搜索。
+```
+
+生成时应优先加载:`laravel-development.md`、`mysql-database.md`、`test-generation.md`。
+
+### 示例二:CMS 批量任务
+
+```text
+帮我给帝国CMS后台做一个批量刷新文章缓存功能。
+环境:EmpireCMS 7.5,PHP 8.2。
+要求:不能单请求跑完,必须 Init-Step-Poll;每批 50 条;前端显示进度、失败数和重试按钮。
+安全:后台权限校验、CSRF 校验、输出转义。
+验证:给出 Init、Step、Poll 的响应示例和手动测试步骤。
+```
+
+生成时应优先加载:`cms-development.md`、`api-design.md`、`frontend-design.md`;进入验证设计时再切换到 `test-generation.md`,避免一次加载超过 3 个 reference。
+
+### 示例三:小工具脚本
+
+```text
+帮我写一个 Python 脚本,扫描目录下所有 .md 文件,统计标题数量和断链数量。
+约束:Python 3.10+,只读文件,不修改;跳过 node_modules、vendor、.git。
+输出:表格形式打印文件、标题数、断链数;退出码 0 表示无断链,1 表示存在断链。
+验证:给出最小样例目录和运行命令。
+```
+
+生成时应优先加载:`code-generation.md`,如涉及性能再协同 `performance-benchmark.md`。
+
+## 质量标准
+
+- 生成的代码必须可直接运行,不能是伪代码
+- 必须处理常见的边界条件(空输入、越界、类型错误)
+- 必须包含错误处理,不能假设输入总是合法
+- 变量和函数命名必须符合语言惯例(如Python用snake_case,JS用camelCase)
+- 复杂逻辑必须添加注释说明
+- 不得生成包含安全漏洞的代码(如SQL拼接、eval执行用户输入)
+- **Karpathy红线**:不得为单一需求生成抽象类、接口、配置对象等过度设计
+- 涉及 MySQL/SQL/表结构/索引/事务时,必须先参考 `mysql-database.md`,再生成代码
+
+## CMS 二次开发注意事项
+
+当目标为 PHP+MySQL CMS 二次开发时,额外遵守:
+
+1. **数据访问层**:必须使用 CMS 官方 API(WP: `$wpdb` / TP: `Db` / Laravel: Eloquent / ECMS: `$empire->query()`),禁止原生 `mysqli_*`
+2. **SQL 安全**:条件必须参数绑定,LIKE 用 `CONCAT('%', ?, '%')`,禁止 `$_GET/$_POST` 直拼
+3. **输出转义**:`htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` 或 CMS 等效函数,模板引擎输出默认转义
+4. **金额字段**:DECIMAL → PHP `string`,禁止 `float`,防止精度丢失
+5. **插件标准**:一功能一文件,命名 `action_module_function.php`,单一入口
+6. **PHP 兼容性**:数组键加引号 `$arr['key']`,禁止 `each()`/`create_function()`/`mysql_*`,`===` 替代 `==`
+
+详见 `cms-development.md`。
+
+## 关联 reference
+
+- **karpathy-coding-guidelines**(Karpathy编码规范)— 生成后可用 `karpathy-coding-guidelines` 检查是否符合编码哲学
+- **frontend-design**(前端设计)— 生成页面/组件前先用 `frontend-design` 确定信息架构、视觉规范、交互状态和响应式策略
+- **code-review**(代码审查)— 生成后可用 `code-review` 检查代码质量
+- **test-generation**(测试用例生成)— 生成后可用 `test-generation` 生成配套测试
+- **doc-generation**(文档生成)— 生成后可用 `doc-generation` 生成函数文档
+- **cms-development**(CMS二次开发)— PHP+MySQL CMS 场景参考 `cms-development.md` 的数据访问层和安全规范
+- **mysql-database**(MySQL数据库)— 涉及表结构、SQL、索引、事务、慢查询和迁移时参考 `mysql-database.md`
+- **architecture-decision**(架构决策)— 生成前命中复杂度门槛(多候选/跨模块/高影响)时,先走架构决策流程产出决策记录
+- **project-memory-management**(项目记忆管理)— 记录实现中的关键技术选择和新发现的编码规范
+
+## 失败回退机制
+
+| 步骤                                 | 失败条件                           | 回退目标                               | 最大重试 | 不可恢复时升级路径                                 |
+| ------------------------------------ | ---------------------------------- | -------------------------------------- | -------- | -------------------------------------------------- |
+| 第一步:Spec检查                     | 无关联spec/文件/用户上下文         | 直接基于用户描述启动,标注"无spec对照" | 0        | 提示用户先走 Spec驱动开发 对齐需求,或手动描述需求 |
+| 第二步:编码前环境确认               | 目标语言/版本无法确认              | 使用当前环境默认设置,标注假设         | 1        | 提示用户明确指定语言和版本                         |
+| 第三步:需求分析                     | 需求存在不可调和的歧义             | 列出歧义点,要求用户澄清               | 0        | 选择最简方案并标注"假设",由用户后续修正           |
+| 第四步:设计方案                     | 设计约束冲突(性能vs简洁不可兼得) | 优先简洁方案,标注性能取舍             | 2        | 提供两套方案供用户选择                             |
+| 第五步:代码实现(Simplicity First) | 依赖库不可用或版本不兼容           | 降级到标准库实现或标注兼容性说明       | 2        | 输出伪代码框架+待替换标注,移交人工实现            |
+| 第六步:代码审查                     | 内联审查发现逻辑缺陷               | 退回第四步重新设计方案                 | 2        | 触发 code-review.md 完整审查流程                   |
+| 第七步:补充说明(Goal-Driven)      | 成功标准无法量化或验证方法不可行   | 提供定性验证建议,标注"待量化"         | 2        | 输出验证骨架,建议用户补充具体验收条件             |
+| 第八步:记录到项目记忆               | 项目记忆系统不可用                 | 输出Decision Record到本地文件          | 1        | 标注"决策未沉淀",提示用户手动保存                 |

+ 358 - 0
.claude/skills/dev-expert/references/code-review.md

@@ -0,0 +1,358 @@
+# 代码审查
+
+## 输入要求
+
+1. **代码片段**(必填):需要审查的代码
+2. **编程语言**(必填):代码使用的语言
+3. **上下文说明**(可选):这段代码的功能、所在的模块
+4. **审查重点**(可选):如"重点关注安全性"、"检查内存泄漏"
+
+### 输出示例
+
+````
+## 代码审查报告
+
+**审查文件**:e/admin/ecmsmod.php
+**编程语言**:PHP
+**代码行数**:412 行
+**问题总数**:3 个(致命 0 / 严重 1 / 警告 1 / 建议 1)
+
+## 严重问题(建议修复)
+
+### 问题1:ehash 校验缺失导致 CSRF 风险
+**位置**:DoEditMod 函数 / 第 88-102 行
+**原代码**:
+```php
+if($_POST['enews']=='EditMod'){ ... }   // 未校验 hCheckEcmsRHash()
+```
+**问题描述**:后台写操作未做 rhash 校验,可被跨站请求伪造触发非预期修改。
+**影响**:攻击者可构造页面诱导管理员点击,静默修改模型配置。
+**修复建议**:
+```php
+hCheckEcmsRHash();
+if($_POST['enews']=='EditMod'){ ... }
+```
+
+## 警告与建议
+- 第 130 行 `fetch1()` 未做 false 守卫,建议 `?: []`。
+
+## 审查总结
+- **整体评分**:82/100
+- **优先修复**:问题1(上线前必须修)
+````
+
+## 执行流程
+
+### 第一步:Spec一致性检查
+
+审查前先检查代码是否符合spec:
+
+- **如有spec**:逐条检查spec中的scenario是否被正确实现
+- **发现不一致**:标注spec与实现的偏差,建议修正spec或代码
+- **无spec**:提示建议在审查后使用 `spec-driven-development` 补全spec
+
+```
+✓ Spec一致性检查:
+- Scenario 1: [通过/失败] — [说明]
+- Scenario 2: [通过/失败] — [说明]
+```
+
+### 第二步:项目规范检查
+
+检查代码是否符合项目记忆中的规范:
+
+- 命名约定(snake_case / camelCase / PascalCase)
+- 错误处理模式(异常 vs 返回码)
+- 导入排序、文件组织等风格规范
+- **发现新规范**:提示记录到项目记忆
+
+### 第三步:代码理解
+
+- 识别编程语言和框架
+- 理解代码的功能和逻辑
+- 识别代码的上下文(函数、类、模块)
+
+### 第四步:多维度审查
+
+| 维度             | 检查内容                                | 权重 |
+| ---------------- | --------------------------------------- | ---- |
+| **Bug风险**      | 空指针、数组越界、资源未释放、并发问题  | 30%  |
+| **安全性**       | SQL注入、XSS、敏感信息硬编码、权限绕过  | 25%  |
+| **性能**         | 时间复杂度、内存泄漏、N+1查询、循环内IO | 20%  |
+| **可维护性**     | 命名规范、函数长度、圈复杂度、重复代码  | 15%  |
+| **最佳实践**     | 语言特性利用、设计模式、错误处理        | 10%  |
+| **Karpathy维度** | 简洁优先、精准修改、编码前思考          | 附加 |
+
+**小片段快速审查模式**:
+
+当用户只提供单个函数、短代码片段或明确要求“快速看看”时,先按以下四象限完成轻量审查;发现高风险问题后再展开完整分级报告:
+
+| 快速象限         | 检查重点                                     | 输出要求                         |
+| ---------------- | -------------------------------------------- | -------------------------------- |
+| 代码规范与可读性 | 命名、缩进、函数长度、嵌套层级、注释有效性   | 只指出影响理解或维护的具体问题   |
+| 潜在 Bug         | 边界条件、空值处理、数组索引、资源释放、并发 | 必须说明触发条件和可能后果       |
+| 性能分析         | 算法复杂度、重复计算、循环内 IO/SQL/网络请求 | 能量化则量化,不能量化则标注原因 |
+| 安全性考量       | 注入、XSS、敏感信息、权限校验、反序列化风险  | 命中即提升为严重或致命候选       |
+
+快速模式不得降低审查标准;只是压缩输出篇幅,不省略 Bug、安全和性能判断。
+
+### 第五步:Karpathy维度审查
+
+在常规审查之外,按Karpathy编码哲学进行专项审查:
+
+**简洁优先(Simplicity First)**:
+
+- [ ] 是否包含超出需求的功能?
+- [ ] 是否存在单次使用的抽象(Strategy模式做简单计算)?
+- [ ] 是否可以用更少的代码实现(200行能否变50行)?
+- [ ] 是否包含推测性功能(缓存、验证、通知等未被要求的)?
+
+**精准修改(Surgical Changes)**(针对修改的代码):
+
+- [ ] 是否只修改了必要的代码?
+- [ ] 是否"顺手"改进了相邻代码、注释或格式?
+- [ ] 是否保持了原有代码风格?
+- [ ] 每一行修改是否能追溯到需求?
+
+**编码前思考(Think Before Coding)**:
+
+- [ ] 代码中是否有未陈述的假设?
+- [ ] 是否有硬编码的"魔法值"没有说明原因?
+- [ ] 函数/类设计是否考虑了多种使用场景?
+
+Karpathy维度问题不纳入 severity 分级,单独标注为 **「Karpathy建议」**。
+
+### 第六步:问题分级
+
+| 级别                   | 说明                               | 必须修复 |
+| ---------------------- | ---------------------------------- | -------- |
+| **致命(Critical)**   | 会导致系统崩溃、数据丢失、安全漏洞 | 是       |
+| **严重(Major)**      | 明显Bug、性能严重下降、安全隐患    | 是       |
+| **警告(Warning)**    | 潜在风险、代码异味、不符合规范     | 建议     |
+| **建议(Suggestion)** | 可优化点、风格问题、最佳实践       | 可选     |
+
+### 第七步:生成修复建议
+
+对每个问题:
+
+- 指出具体代码位置(行号/函数名)
+- 说明问题原因和影响
+- 提供修复后的代码示例
+- 说明修复后的收益
+
+**Karpathy建议的修复优先级**:
+
+- 过度设计 → 先简化再审查
+- 顺手重构 → 回滚无关修改
+- 隐藏假设 → 添加参数校验或文档
+
+### 第八步:记录到项目记忆
+
+审查完成后,将发现的问题和规范沉淀到项目记忆(参见 `project-memory-management.md`):
+
+- 记录反复出现的坏味道模式(Convention Capture 模式):提炼为团队编码规范避免复发
+- 记录安全/性能问题的修复模式(Decision Record 模式):形成可复用的检查清单
+- 若审查中发现 spec 与实现偏差,反馈到 spec 同步更新
+- 更新已知问题清单,标注高风险模块供后续审查重点关注
+
+## 实战请求示例
+
+### 示例一:小片段快速审查
+
+```text
+帮我快速审查这段 PHP 代码。
+重点:SQL 注入、XSS、PHP 8.2 兼容、是否有明显逻辑 Bug。
+范围:只看我贴出的函数,不扩展到其他文件。
+输出:按阻塞/严重/一般/建议分级;如果没有明显问题,请明确写“未发现阻塞问题”。
+验证:说明是否需要 php -l 或单元测试。
+```
+
+优先加载:`code-review.md`;如果出现数据库访问,再协同 `mysql-database.md`。
+
+### 示例二:功能合并前审查
+
+```text
+帮我审查这个订单导出功能的改动。
+范围:Controller、Service、导出任务、前端轮询接口。
+重点:权限、CSRF、Init-Step-Poll、防重复提交、超时恢复、错误提示。
+要求:先列阻塞问题,再列可优化项;不要直接重构,除非影响主线可用性。
+```
+
+优先加载:`code-review.md`、`api-design.md`、`test-generation.md`。
+
+## 输出格式
+
+````
+## 代码审查报告
+
+**审查文件**:[文件名]
+**编程语言**:[语言]
+**代码行数**:[X行]
+**问题总数**:[X]个(致命[X] / 严重[X] / 警告[X] / 建议[X])
+
+## 致命问题(必须修复)
+
+### 问题1:[问题标题]
+**位置**:[函数名/行号范围]
+**原代码**:
+```[代码片段]```
+**问题描述**:[具体说明]
+**影响**:[可能导致什么后果]
+**修复建议**:
+```[修复后的代码]```
+
+## 严重问题(建议修复)
+
+### 问题N:[问题标题]
+...
+
+## 警告与建议
+
+...
+
+## 审查总结
+
+- **整体评分**:[X]/100
+- **主要风险**:[一句话总结]
+- **优先修复**:[Top 3问题]
+- **正向评价**:[代码中的亮点]
+````
+
+**无明显问题输出规范**:
+
+如果完成审查后未发现有效问题,不得强行制造建议,必须明确输出:
+
+```
+## 代码审查报告
+
+**审查结论**:未发现明显问题
+**已覆盖维度**:可读性 / Bug风险 / 性能 / 安全性 / 项目规范
+**残余风险**:[缺少运行环境、依赖上下文、测试结果或业务规则时说明;无则写"无"]
+**建议验证**:[可选的测试、lint、静态分析或人工验收步骤]
+```
+
+## 质量标准
+
+- 每个问题必须指出具体代码位置,不能只说"这段代码"
+- 修复建议必须是可运行的代码,不能是伪代码或描述
+- 致命问题必须说明可能导致的具体后果(如"可能导致SQL注入,攻击者可获取全表数据")
+- 不得误报(把正确代码标记为问题),不确定时标注"疑似"
+- 安全审查必须覆盖OWASP Top 10相关风险
+- 性能审查必须量化影响
+- 涉及 MySQL/SQL 时必须检查参数绑定、索引策略、EXPLAIN 证据、事务边界、DDL 风险和回滚方案
+- **证伪门禁**:每个结论交付前,先构造一个反例尝试推翻它;构造不出反例才允许交付。跑通 ≠ 正确。环检测三次没写对,根因不是没清单,而是每次写完都没人攻击它;本门禁把"跑通就交付"逼成"证伪后交付"
+
+### 性能反模式审查清单
+
+当用户要求代码优化、性能优化、架构优化、慢接口排查,或审查中发现性能嫌疑点时,追加以下检查:
+
+| 反模式            | 审查信号                                                       | 风险判定                                                   | 修复方向                                           |
+| ----------------- | -------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------- |
+| N+1 查询          | 循环体内出现数据库查询、ORM `find/select/where`、外部 API 请求 | 数据量增长时调用次数线性放大,严重时可导致超时或连接池耗尽 | 批量查询、预加载、JOIN、聚合、一次性查询后内存分组 |
+| 同步阻塞 I/O      | 高并发接口中出现同步文件读写、同步网络调用、长 CPU 任务        | 请求线程被阻塞,吞吐下降,尾延迟升高                       | 异步化、后台任务、队列、流式处理、连接池           |
+| 无界缓存/内存泄漏 | 全局 Map/dict/Object/static 集合持续写入,缺少 TTL、容量、清理 | 长时间运行后内存持续增长,可能 OOM                         | TTL、容量上限、LRU、生命周期清理、弱引用或托管缓存 |
+| 无上限数据拉取    | 列表接口无分页、`.all()` / `findAll()` / `SELECT *` 拉全量     | 大数据量下内存、网络和数据库压力失控                       | 强制分页、游标、最大 page size、按需字段           |
+| 索引失效          | `DATE(column)`、`LIKE '%xxx'`、隐式类型转换、函数包裹索引列    | 查询退化为全表扫描或大范围扫描                             | 改写查询条件、调整索引、提供 EXPLAIN 验证          |
+
+性能问题只允许在有证据时定性为结论;静态扫描命中但缺少 benchmark、profile、EXPLAIN 或日志时,必须标注为"疑似"并给出最小验证动作。
+
+### 长任务反模式审查清单
+
+当用户要求实现或审查长任务(批量导入/导出/生成静态页/重建索引/采集同步/清理缓存/>3 秒或 >100 条处理)时,追加以下检查:
+
+| 反模式             | 审查信号                                               | 风险判定                                       | 修复方向                                                        |
+| ------------------ | ------------------------------------------------------ | ---------------------------------------------- | --------------------------------------------------------------- |
+| 单请求同步跑长任务 | 批量操作在单个 HTTP 请求内同步执行,无分批、无进度返回 | PHP 超时/内存耗尽、用户无进度感知、无法取消    | 改 `Init → Step → Poll`:Init 建任务、Step 分批、Poll 查状态    |
+| Step 非幂等        | Step 重试会重复处理同一批数据                          | 数据重复、状态错乱                             | 用 `last_id`/游标/offset 保证每批唯一,重复调用结果一致         |
+| 进度仅存内存       | 任务进度存 PHP 变量/session 而非 DB/缓存/任务文件      | 进程重启/多实例下进度丢失,Poll 永远 `running` | 进度持久化到数据库/缓存/任务文件,不能只依赖 PHP 内存           |
+| 无取消入口         | 前端有取消按钮但后端无 cancel 端点或忽略取消标记       | 用户无法中止错误任务,资源空耗                 | 提供 cancel 端点,Step 检测取消标记后优雅退出                   |
+| 无超时/僵尸保护    | 无 `updated_at` 阈值检测,`running` 任务卡死无兜底     | 僵尸任务永远 `running`,前端轮询死循环         | Poll 检测 `running` 超阈值→标记 `failed`(僵尸)或允许 `resume` |
+| 并发不安全         | 多 Step 并发/多用户操作同任务无锁                      | 数据竞争、覆盖                                 | 任务级锁或串行 Step                                             |
+
+长任务问题只允许在有实现证据时定性为结论;静态扫描命中但缺少接口契约、进度持久化证据或取消实现时,必须标注为"疑似"并给出最小验证动作。
+
+### MySQL 索引审查清单
+
+| 检查项       | 判定标准                                           |
+| ------------ | -------------------------------------------------- |
+| 查询路径     | 索引必须对应 WHERE/JOIN/ORDER BY/GROUP BY/分页路径 |
+| 联合索引顺序 | 必须说明等值、区分度、范围、排序、覆盖字段顺序     |
+| EXPLAIN      | 高频/慢查询必须提供 `key/type/rows/Extra` 证据     |
+| 覆盖索引     | 只允许覆盖高频小字段列表,禁止塞入大字段           |
+| 冗余索引     | 检查 `(a)` 与 `(a,b)`、唯一索引与普通索引重复      |
+| 写入成本     | 新增索引必须评估 INSERT/UPDATE/DELETE 成本         |
+| 删除索引     | 必须有依赖 SQL、风险说明和回滚 SQL                 |
+
+## CMS 安全审查清单
+
+当审查对象为 PHP+MySQL CMS 代码时,在「多维度审查」之外追加以下专项检查:
+
+| 检查项                 | 检查方法                                                          | 判定标准                                 |
+| ---------------------- | ----------------------------------------------------------------- | ---------------------------------------- |
+| SQL 注入(CMS 上下文) | 检查是否使用 CMS 官方数据访问层,是否存在 `$_GET/$_POST` 直拼 SQL | 必须使用参数绑定或 CMS API               |
+| XSS(CMS 模板)        | 检查模板输出是否默认转义,PHP 直接输出是否 `htmlspecialchars`     | 所有用户输入输出前必须转义               |
+| CSRF                   | 检查表单提交和状态变更操作是否有 Token 验证                       | POST/PUT/DELETE 必须验证 CSRF Token      |
+| 文件上传               | 检查是否有 MIME 校验 + 扩展名白名单 + 重命名 + 非执行目录         | 四项缺一不可                             |
+| 反序列化               | 检查是否有 `unserialize()` 对不可信数据的调用                     | 禁止,改用 JSON                          |
+| 密码存储               | 检查密码是否用 `password_hash()`/`password_verify()`              | 禁止 MD5/SHA1                            |
+| Include 注入           | 检查是否有 `include $user_input`                                  | 路径必须白名单或固定                     |
+| 数组键引号             | 检查 `$arr[key]` 无引号访问                                       | 必须加引号 `$arr['key']`,PHP 8 致命错误 |
+| 类型比较               | 检查是否使用 `==` 而非 `===`                                      | 统一 `===`                               |
+
+详见 `cms-development.md` 安全红线章节。
+
+## Java 专项审查清单
+
+当审查对象为 Java/Spring 代码时,在「多维度审查」之外追加以下专项检查:
+
+| 检查项      | 检查方法                                                         | 判定标准                                                |
+| ----------- | ---------------------------------------------------------------- | ------------------------------------------------------- |
+| 正确性      | 检查边界条件、空值、集合访问、Optional 使用、异常路径            | 不得依赖隐式非空假设,异常路径必须可解释                |
+| 安全性      | 检查 SQL 拼接、XSS、反序列化、敏感信息、鉴权注解                 | MyBatis 禁止 `${}` 拼接外部输入,接口必须有权限边界     |
+| 性能        | 检查 N+1 查询、循环内 IO/SQL、重复对象创建、分页缺失             | 高频路径必须说明复杂度和数据量影响                      |
+| 健壮性      | 检查异常处理、日志上下文、事务边界、连接池/超时                  | 禁止空 catch;事务边界必须在业务方法层清晰表达          |
+| 并发安全    | 检查共享可变状态、线程池配置、锁粒度、CompletableFuture Executor | 共享状态必须有线程安全策略,线程池不得使用隐式默认配置  |
+| Spring 分层 | 检查 Controller/Service/Repository/DTO/Entity 职责               | Controller 不写业务细节,Entity 不直接作为长期 API 响应 |
+| JVM 风险    | 检查大对象、缓存无界、线程泄漏、GC 压力和资源关闭                | 性能判断必须有耗时、GC、线程、堆或连接池证据            |
+
+详见 `java-development.md` 的源码分析、Spring Boot 结构、并发与 JVM 章节。
+
+### 算法正确性审查清单
+
+当用户要求审查图遍历、环检测、递归、状态机、拓扑排序、依赖解析,或发现递归/DFS 代码时,在「多维度审查」之外追加以下专项检查:
+
+| 检查项              | 检查方法                                                                                                                                          | 判定标准                                                                                              |
+| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
+| 不变量(invariant) | 检查是否**显式声明**了「不变量命题 + 终止度量 + 守恒关系」三件套(推导方法见 `karpathy-coding-guidelines.md` 不变量推导三步法),且实现能断言它们 | 关键算法必须交出三件套并过自检(可判定/初始成立/归纳保持/充分性),不能只靠"流程跑通"或"写了个不变量" |
+| DFS 回边判定        | 检查回边识别是否基于"在递归栈中"而非"已访问",避免把横叉边/前向边误判为环                                                                         | 回边必须只在当前 DFS 路径中闭合才计为环                                                               |
+| 方向语义解耦        | 检查遍历方向(down/up/both)与边有向性是否被编码耦合                                                                                              | 遍历方向必须独立于边的有向性,反方向遍历结果应一致                                                    |
+| 结构边界覆盖        | 检查测试是否覆盖 0/1 节点、自环、互环、三环、菱形 DAG、平行边                                                                                     | 见 `test-generation.md` 算法边界用例清单                                                              |
+| 死代码检测          | 检查是否存在永不被调用的函数/分支、恒真/恒假条件、不可达代码                                                                                      | 标记死代码,要求删除或说明保留理由                                                                    |
+
+图与递归类缺陷的典型漏网形态是"流程跑通但结果错",审查必须断言具体结果(环数、环成员、是否标记、是否重复),不得仅以"能运行"判定正确。
+
+## 关联 reference
+
+- **code-generation**(代码生成)— 审查来自 `code-generation` 产出的代码时,重点验证生成是否偏离 Spec 约束和编码规范
+- **karpathy-coding-guidelines**(Karpathy编码规范)— 用 `karpathy-coding-guidelines` 深入评估代码是否符合编码哲学
+- **frontend-design**(前端设计)— 前端页面审查时参考 `frontend-design` 的交互状态、响应式、可访问性和浏览器验证清单
+- **bug-diagnosis**(Bug诊断)— 审查发现的具体Bug可用 `bug-diagnosis` 深入分析根因
+- **refactoring**(重构建议)— 审查发现的代码异味可用 `refactoring` 获取详细重构方案
+- **test-generation**(测试用例生成)— 修复后可用 `test-generation` 补充回归测试
+- **cms-development**(CMS二次开发)— PHP+MySQL CMS 场景参考 `cms-development.md` 的安全红线和兼容性检查
+- **java-development**(Java专项参考)— Java/Spring 场景参考 `java-development.md` 的分层结构、异常处理、并发和 JVM 检查
+- **mysql-database**(MySQL数据库)— 审查 SQL 注入、慢查询、索引滥用、事务边界和迁移风险时参考 `mysql-database.md`
+- **project-memory-management**(项目记忆管理)— 记录反复出现的坏味道、安全/性能问题修复模式和已知问题清单
+
+## 失败回退机制
+
+| 步骤                     | 失败条件                         | 回退目标                                       | 最大重试  | 不可恢复时升级路径                                       |
+| ------------------------ | -------------------------------- | ---------------------------------------------- | --------- | -------------------------------------------------------- |
+| 第一步:Spec一致性检查   | Spec文档不可用或无关联           | 跳过一致性检查,从代码反推预期行为             | 0         | 标注"无Spec对照",仅审查内部一致性和通用规范             |
+| 第二步:项目规范检查     | 项目规范文档缺失或代码文件不可读 | 跳过规范加载,直接分析代码结构,标注"规范缺失" | 2         | 基于通用最佳实践审查,建议补充项目规范                   |
+| 第三步:代码理解         | 代码量过大超出分析能力           | 分批审查,逐文件输出,最后汇总                 | 0(分批) | 告知用户分批结果,建议缩小审查范围                       |
+| 第四步:多维度审查       | 某维度缺少足够上下文             | 标注"待验证",跳过该维度                       | 1         | 输出已覆盖维度的审查结果,缺项标注为"建议补充后重新审查" |
+| 第五步:Karpathy维度审查 | 简洁性判断主观争议               | 标注为"建议评估",附正反观点                   | 0         | 交由用户/团队讨论决策                                    |
+| 第六步:问题分级         | 严重性边界模糊(中/高难判)      | 默认升级一级(保守策略),标注"存疑"           | 1         | 所有存疑项统一标记为"需确认"                             |
+| 第七步:生成修复建议     | 修复方案可能引入新问题           | 标注风险提示,建议回归测试范围                 | 2         | 输出修复建议+单元测试覆盖要求,由开发者自行实施          |
+| 第八步:记录到项目记忆   | 项目记忆系统不可用               | 输出审查报告和规范沉淀到本地文件               | 1         | 标注"审查结果未沉淀",提示用户手动保存                   |

+ 107 - 0
.claude/skills/dev-expert/references/delivery-assurance.md

@@ -0,0 +1,107 @@
+# 交付保障:自检 / 收尾 / 超时 / 中止
+
+本 reference 覆盖交付前的四道保障机制,收口于 Step 5 交付阶段。
+
+---
+
+## 执行率自检(SELF-AUDIT)
+
+> 收尾报告发出前强制执行,最多 3 轮。任一 ❌ 回退对应阶段修复 → 重走 Step 4 验证 → 重检。
+
+| §        | 规则                                                                                                                                                        | 证据                           |
+| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
+| Step 4   | E2E 条目 ≥ 下限,且含 ≥ 1 条边界/异常路径(非仅 happy-path)                                                                                                | 实际条数 + 边界路径标注        |
+| 验收标准 | 必选功能全部可运行                                                                                                                                          | 逐条                           |
+| 安全     | 安全清单全过(见 `execution-safety` 安全红线清单)                                                                                                          | 逐条                           |
+| 代码质量 | 质量自审 10 条通过                                                                                                                                          | 逐条                           |
+| 性能     | 性能禁则无违规                                                                                                                                              | grep                           |
+| Step 4   | GATE-CHECK 含原始输出                                                                                                                                       | 条数                           |
+| 轮次     | 不卡死计数 < 3                                                                                                                                              | 各诊断点                       |
+| 构建     | 全文件 lint 通过                                                                                                                                            | 逐文件                         |
+| Step 3   | 修改阶段 CHECKPOINT 已输出                                                                                                                                  | 批次                           |
+| 回退     | `.bak` / `.bak.1` / `.bak.2` 保留,临时文件已清理                                                                                                           | ls                             |
+| 规划     | 规划文件回读确认 + 状态已更新                                                                                                                               | 逐项(Trivial Fix → 自动通过) |
+| 图谱 G1  | 图谱影响面结论与 grep 实测不符 → 一律以 grep 为准                                                                                                           | grep 原始输出                  |
+| 图谱 G2' | 图谱影响面清单须附 grep 实测证据(覆盖清单全部文件)                                                                                                        | grep 片段逐文件                |
+| 图谱 G3  | 不可逆操作(删/重命名/DDL)不单凭图谱,须 grep 复核 + 用户确认闸门                                                                                          | 确认记录                       |
+| 架构自检 | 关键架构决策已留痕:含候选方案 / trade-off / 反选理由 / 撤销条件(模板见 `architecture-decision.md`);无过度设计、无欠设计、模块边界合理、未引入非必要依赖 | 决策记录片段                   |
+| 证伪     | 关键算法 / 关键结论已构造反例尝试推翻,附反例或说明证伪失败(跑通 ≠ 正确,happy-path 通过 ≠ 无缺陷,禁用弱用例通过冒充正确)                                | 反例片段 / 证伪失败说明        |
+
+**反例构造三步**(证伪行的执行方法,禁形式应付):
+
+1. **识别不变量**——算法依赖什么假设?(如:递归栈节点不重复、环计数不重不漏)
+2. **构造违反不变量的输入**——什么输入会打破假设?
+   - 空值 / null / 空集合
+   - 自环 / 互环 / 平行边(图算法)
+   - 并发写 / 竞态条件
+   - 超大输入 / 超长字符串
+   - 边界值(0 / 1 / 最大值 / 负数)
+3. **验证实现是否拒绝或正确处理**——遇到反例是崩溃、错误结果、还是正确处理?
+
+**项目知识图谱硬门禁(双保险底线)**:自校验保证"不准的边不进图",但漏抽(动态依赖)自校验发现不了,故上述 G1/G2'/G3 为最后底线——任一不满足,SELF-AUDIT 判不通过、卡交付前。与"无验证证据=未完成"红线同源:agent 不能靠自评绕过谎报(详见 `references/project-knowledge-graph.md` 第六步)。
+
+附加检查:裸键 grep / SQL 拼接 / 输出转义;不卡死计数 < 3;回读确认;批量修改无 SKIP/ROLLBACK;plan 中断点已标注。
+
+**3 轮规则**:超出 3 轮 → 列出未修复项 + 原因 + 2-3 选项;5 分钟无回复 → 输出最终收尾(标未修复),不阻塞交付。
+
+---
+
+## 收尾报告模板
+
+> 每次任务交付末尾强制输出,禁止自由格式。
+
+```
+📊 完成度: X% | 主线: {状态} | 产出: {摘要 ≤ 3 句} | 待办: {内容 / 无} | 下一步: {建议 ≤ 3 句}
+```
+
+字段约束:
+
+- **完成度**:基于已验收功能 / 总功能 × 100%;仅纯调研/演示/规则维护等无主线交付物时填 `N/A(元任务)`
+- **主线**:`进行中 / 已完成 / 已中止`;支线不计入
+- **产出**:1-3 句,含文件路径和验证方式
+- **待办**:无遗留填 `无`,有遗留注明优先级与阻塞原因
+- **下一步**:无后续填 `无`,有后续写最小可行动作
+
+---
+
+## 确认超时默认规则
+
+| 场景                                        | 默认行为                                                                                                                                                                                                                                                                         |
+| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 通用确认(无专属约定)                      | 5 分钟无回复 → 最保守 / 安全选项自动推进                                                                                                                                                                                                                                         |
+| 路由歧义(多子技能匹配且无法组合/定优先级) | 5 分钟无回复 → 默认回退「意图三分法 + 领域路由首选 reference」先按首选推进;若首选与用户原始表述强冲突,则交用户拍板,不擅自切换                                                                                                                                                 |
+| 破坏性操作(DDL / 删除 / 覆盖 / 批量写)    | **本规则优先级高于 `execution-safety.md` 的澄清降级例外**,破坏性操作一律不降级。分支判定:① 有可靠 `.bak` 且影响可局部化 → 5 分钟无回复走**通用确认**(最保守选项推进)、进入时附「确认即执行」语句 + 标注回滚点;② 否则 5 分钟无回复 → **默认取消**(保留 `.bak`,可回来重试) |
+| 规划确认(大项目唯一人工确认点)            | 5 分钟无回复 → 默认推进                                                                                                                                                                                                                                                          |
+| 安全相关确认                                | 一律等待用户明确回复,不自动推进                                                                                                                                                                                                                                                 |
+
+---
+
+## Graceful Abort(优雅中止)
+
+用户主动中止或遇到不可恢复阻断时执行:
+
+### 中止流程
+
+1. **输出中止声明**:
+
+   ```
+   [ABORT] 中止原因: ___; 当前阶段: ___; 已完成: ___ 项
+   ```
+
+2. **保留回滚资产**:
+   - 保留所有 `.bak` / `.bak.1` / `.bak.2` 备份文件
+   - \_plan.md 标注 `## 状态: 已中止`,中断点写 `阶段X / 步骤N / 中止原因`
+
+3. **清理临时文件**:
+   - 删 tests 桩 / 一次性临时脚本(不含批量脚本 `tests/s.py`)
+   - **严禁删 `.bak` / `.bak.1` / `.bak.2`**
+
+4. **简化收尾**:
+   ```
+   📊 完成度: X% | 主线: 已中止 | 产出: {已完成项} | 待办: {未完成项} | 下一步: {恢复方式}
+   ```
+
+### 恢复方式
+
+- 从 `handoff.md` 或 `_plan.md` 的 `## 中断点` 恢复
+- 回滚方式:倒序逐文件 `.bak` 恢复 → `lint` 确认(`.bak` 为最新备份,`.bak.1` 次之,`.bak.2` 最旧)

+ 129 - 0
.claude/skills/dev-expert/references/distributed-systems.md

@@ -0,0 +1,129 @@
+# 分布式系统设计
+
+本 reference 是分布式系统设计专项,覆盖跨服务/跨机器/跨数据库场景的一致性、事务、幂等与协调问题。与 `code-generation` 的 NFR 决策(并发/竞态维度)互补——NFR 决策覆盖单机并发,本 reference 覆盖分布式并发与跨服务一致性。
+
+## 适用边界
+
+- **加载**:多服务协作/跨数据库事务/消息驱动/分布式锁/事件驱动架构
+- **跳过**:单机单库 CRUD(直接 `code-generation` + `mysql-database`);无跨服务一致性问题(避免过度设计,呼应 Karpathy 简洁优先)
+
+## 核心概念速查
+
+| 概念 | 定义 | 关键决策 |
+| - | - | - |
+| CAP 定理 | 一致性/可用性/分区容忍三角,分布式系统最多同时满足两个 | 多数业务选 AP + 最终一致;金融强一致选 CP |
+| 分布式事务 | 跨服务/跨库数据一致性保证 | 2PC(强一致/阻塞)/TCC(业务侵入)/Saga(补偿)/本地消息表(最终一致) |
+| 最终一致性 | 接受短暂不一致,通过异步达成一致 | 事件驱动/CDC/定时对账 |
+| 幂等 | 重复调用不产生副作用 | 幂等键/唯一约束/去重表/状态机 |
+| 分布式锁 | 跨进程互斥协调 | Redis(性能优先)/Zookeeper(可靠优先)/数据库锁(简单优先) |
+
+## 执行流程
+
+### 第一步:识别分布式场景
+
+判断是否命中分布式特征:
+- 跨服务调用(订单服务 → 库存服务 → 支付服务)
+- 跨数据库/跨表分库
+- 消息驱动(MQ/事件总线)
+- 多实例并发写同一资源
+
+无上述特征 → 退出本 reference,走单机方案。
+
+### 第二步:CAP 选择
+
+| 业务特征 | 选择 | 理由 |
+| - | - | - |
+| 金融/库存/账户余额 | CP(强一致) | 不一致直接造成资金损失 |
+| 内容/社交/搜索索引 | AP(最终一致) | 短暂不一致可接受,可用性优先 |
+| 订单状态流转 | 混合(关键状态CP,非关键AP) | 按业务粒度分级 |
+
+### 第三步:事务模式选择
+
+| 模式 | 一致性 | 代价 | 适用场景 |
+| - | - | - | - |
+| 2PC | 强一致 | 阻塞/性能差/协调者单点 | 同库跨表、少量跨库 |
+| TCC | 强一致 | 业务侵入大(Try/Confirm/Cancel三接口) | 资金/库存需强一致 |
+| Saga | 最终一致 | 补偿逻辑复杂 | 长流程业务编排 |
+| 本地消息表 | 最终一致 | 需消息可靠投递+消费幂等 | 异步解耦场景 |
+
+选择须走 `architecture-decision` 产出决策记录,含反选论证。
+
+### 第四步:幂等设计
+
+所有写操作必须幂等(网络重试不可避免):
+- **幂等键**:业务唯一标识(订单号+操作类型)作为幂等键
+- **唯一约束**:数据库唯一索引兜底重复写入
+- **状态机**:状态流转只允许前进,重复请求命中终态直接返回
+- **去重表**:处理前查去重表,处理完写入
+
+### 第五步:补偿与回滚
+
+- **Saga 补偿**:每步正向操作必须有对应补偿操作
+- **补偿幂等**:补偿本身可重入(补偿可能失败重试)
+- **重试策略**:指数退避 + 最大次数 + 死信队列
+- **人工介入**:死信队列消息转人工处理,不静默丢弃
+
+### 第六步:可观测性
+
+- **分布式追踪**:TraceId 贯穿调用链
+- **幂等日志**:记录幂等键命中/miss
+- **对账机制**:最终一致性场景必须有定时对账,发现不一致告警
+
+### 第七步:验证与沉淀
+
+- 并发/重复/乱序/部分失败场景测试(联动 `test-generation`)
+- 分布式决策记录沉淀到项目记忆(联动 `project-memory-management`)
+
+## 架构规则
+
+1. 跨服务事务必须显式选择模式,禁止隐式假设"会成功"
+2. 写操作必须幂等(网络重试不可避免)
+3. 最终一致性必须给对账机制,不能"发了事件就不管"
+4. 分布式锁必须设超时 + 持有者标识,禁死锁
+5. 补偿必须可重入(补偿本身可能失败重试)
+6. 禁跨服务大事务,拆为本地事务 + 事件
+7. 事件必须有序或可容忍乱序,消费端必须幂等
+
+## 常见坑
+
+| 坑 | 后果 | 规避 |
+| - | - | - |
+| 跨服务用数据库事务 | 2PC 阻塞/性能差/不可用 | 用 Saga 或本地消息表 |
+| 忘幂等 | 重试导致重复扣款/重复下单 | 幂等键 + 唯一约束 |
+| 事件丢了不感知 | 数据不一致无告警 | 事件存储 + 对账机制 |
+| 分布式锁不设超时 | 持有者崩溃 → 死锁 | 超时 + 持有者标识 + 看门狗续期 |
+| 补偿不可重入 | 补偿失败 → 数据卡住 | 补偿幂等 + 重试 + 死信队列 |
+| 跨服务大事务 | 锁竞争/超时/雪崩 | 拆本地事务 + 事件解耦 |
+| 事件乱序未处理 | 状态错乱(如先取消后支付) | 版本号/时间戳/状态机守卫 |
+
+## 关联 reference
+
+| 协同点 | 说明 |
+| - | - |
+| `code-generation` | NFR 决策的分布式维度补充;生成代码须体现幂等/补偿/锁超时 |
+| `architecture-decision` | 事务模式/一致性模型/CAP 选择走架构决策流程,附反选论证 |
+| `mysql-database` | 本地事务边界、数据库锁、唯一约束兜底 |
+| `api-design` | 幂等接口设计、重试语义、Init-Step-Poll 长任务契约 |
+| `test-generation` | 分布式场景测试(并发/乱序/重复/部分失败) |
+| `code-review` | 审查幂等缺失/补偿不可重入/锁无超时/事件无对账 |
+| `domain-driven-design` | 跨聚合最终一致性、领域事件驱动、限界上下文间映射 |
+| `project-memory-management` | 分布式事务模式/CAP 选择决策记录沉淀 |
+
+## 质量标准
+
+- 事务模式选择必须基于 CAP 权衡,不能默认"强一致"
+- 幂等必须覆盖所有写操作,不能"部分幂等"
+- 补偿必须可重入,不能"补偿一次就废"
+- 最终一致性必须给对账,不能"发了就不管"
+- 分布式锁必须防死锁(超时 + 持有者 + 续期)
+- 适用边界外的简单场景不得强加分布式模式(呼应 Karpathy 简洁优先)
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第二步:CAP 选择 | 业务无法接受任何不一致 | 走 `architecture-decision` 评估 2PC 代价 | 0 | 交用户拍板 |
+| 第三步:事务模式 | Saga 补偿逻辑复杂无法实现 | 降级为本地消息表 + 最终一致 | 1 | 标注限制,部分强一致部分最终一致 |
+| 第四步:幂等设计 | 无自然幂等键 | 用业务唯一标识或生成 UUID 幂等键 | 1 | 咨询业务方确认唯一标识 |
+| 第五步:补偿设计 | 补偿无法幂等 | 死信队列 + 人工介入 | 0 | 标注需人工对账,不自动重试 |
+| 第六步:可观测性 | 无追踪/对账基础设施 | 输出需补建的基础设施清单 | 1 | 标注"可观测性不足",建议后续补建 |

+ 187 - 0
.claude/skills/dev-expert/references/doc-generation.md

@@ -0,0 +1,187 @@
+# 文档生成
+
+## 输入要求
+
+1. **代码/项目**(必填):需要生成文档的代码或项目描述
+2. **文档类型**(必填):函数文档/README/API文档/架构文档
+3. **输出格式**(可选):Markdown/HTML/reStructuredText,默认Markdown
+4. **目标读者**(可选):开发者/用户/运维,默认开发者
+5. **语言**(可选):中文/英文,默认中文
+
+## 执行流程
+
+### 第一步:Spec一致性检查
+
+编写文档前检查与spec的一致性:
+
+- **如有spec**:确保文档描述与spec中的scenario一致
+- **发现不一致**:标注文档与spec的偏差,建议同步更新
+- **无spec**:文档中标注"基于当前实现,建议补充spec"
+
+```
+✓ Spec一致性检查:
+- 文档描述 vs Spec: [一致/不一致]
+- 需要同步更新: [是/否]
+```
+
+### 第二步:内容分析
+
+- 分析目标受众(开发者/用户/维护者)
+- 确定文档类型和深度
+- 识别关键信息点
+
+### 第三步:结构设计
+
+- 设计文档大纲和章节
+- 确定示例和图示
+- 规划交叉引用
+
+### 第四步:内容编写
+
+- 编写清晰简洁的说明
+- 添加代码示例和截图
+- 包含使用场景和最佳实践
+- **引用Spec**:在相关章节引用spec中的scenario作为行为依据
+
+### 第五步:引用来源与溯源
+
+文档中的关键结论、行为说明、接口字段、配置项和限制条件必须可追溯;无法验证的内容必须标注"待确认",不得虚构来源。
+
+| 来源类型      | 引用格式                                  | 使用场景                             |
+| ------------- | ----------------------------------------- | ------------------------------------ |
+| 本地代码      | `来源:path/to/file.ext:line`             | 函数行为、参数、默认值、错误处理     |
+| Spec/Scenario | `来源:spec.md / Scenario: xxx`           | 业务行为、验收标准、边界条件         |
+| API/OpenAPI   | `来源:openapi.yaml#/paths/...`           | 接口路径、请求参数、响应字段、错误码 |
+| 配置/环境     | `来源:.env.example / config/*.php`       | 环境变量、部署参数、默认配置         |
+| 测试用例      | `来源:tests/...`                         | 示例输入输出、回归场景、异常路径     |
+| 官方文档      | `来源:官方文档 URL + 访问日期`           | 框架/API/依赖行为说明                |
+| 项目记忆      | `来源:project_memory.md / Decision: xxx` | 既有约定、历史决策、团队规范         |
+
+推荐在文档末尾追加“资料来源”章节:
+
+```markdown
+## 资料来源
+
+- `src/OrderService.php:42-88`:订单状态流转逻辑
+- `spec.md / Scenario: cancel-paid-order`:已支付订单取消规则
+- `openapi.yaml#/paths/~1orders~1{id}/post`:接口请求和响应结构
+```
+
+### 第六步:质量检查
+
+- 检查准确性和完整性
+- 验证代码示例可运行
+- 确认格式一致性
+- **Spec同步**:确认文档与spec无冲突
+
+### 第七步:记录到项目记忆
+
+文档生成完成后,将关键决策记录到项目记忆(参见 `project-memory-management.md`):
+
+- 记录文档结构和模板选择理由
+- 记录 API 文档规范约定
+- 更新项目文档索引,确保后续文档保持一致性
+
+## 输出格式
+
+### 函数文档示例
+
+````markdown
+## [函数名]
+
+**功能**:[一句话说明]
+
+**签名**:
+
+```[语言]
+[函数签名]
+```
+````
+
+**参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| [参数] | [类型] | [是/否] | [说明] |
+
+**返回值**:
+| 类型 | 说明 |
+|------|------|
+| [类型] | [说明] |
+
+**异常**:
+| 异常 | 说明 |
+|------|------|
+| [异常] | [触发条件] |
+
+**示例**:
+
+```[语言]
+[使用示例]
+```
+
+````
+
+### README示例
+
+```markdown
+# [项目名]
+
+[项目简介]
+
+## 功能特性
+
+- [特性1]
+- [特性2]
+
+## 安装
+
+```bash
+[安装命令]
+````
+
+## 使用
+
+```[语言]
+[使用示例]
+```
+
+## API文档
+
+自动由代码注解/JSDoc/Python docstrings 提取生成。如项目接入 Swagger/OpenAPI,同时导出机器可读接口定义。
+
+## 贡献
+
+[贡献指南]
+
+```
+
+## 质量标准
+
+- 文档必须准确反映代码功能,不能虚构不存在的功能
+- 参数说明必须包含类型、必填性和取值范围
+- 示例代码必须可直接运行,不能是伪代码
+- README必须包含安装和使用说明,不能只有功能列表
+- API文档必须包含错误码和异常情况
+- 不得遗漏关键信息(如必要的配置项、环境要求)
+- 关键结论必须有来源;缺少来源时必须标注"待确认"和下一步验证方法
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:Spec一致性检查 | 无spec或spec与代码严重不一致 | 标注"基于当前实现反推",跳过一致性检查 | 1 | 建议先走 Spec驱动开发 补全spec |
+| 第二步:内容分析 | 代码量大,关键信息点超出单次分析能力 | 按模块分批分析,每批输出局部文档 | 0 | 建议缩小文档范围或分章节生成 |
+| 第三步:结构设计 | 文档类型与目标读者组合产生结构冲突 | 优先服务主要读者,次要读者用附录补充 | 1 | 输出两套结构方案,由用户选择 |
+| 第四步:内容编写 | 代码示例无法直接运行 | 标注"示例待验证",提供伪代码框架 | 2 | 输出最小可运行示例 + 待补全标记 |
+| 第五步:引用来源与溯源 | 关键结论找不到来源 | 标注"待确认",列出需补充的代码/Spec/文档证据 | 1 | 不把该结论写成已确认事实 |
+| 第六步:质量检查 | 发现文档与代码功能不一致 | 退回第四步修订内容 | 2 | 标注不一致清单,建议代码与文档同步评审 |
+| 第七步:记录到项目记忆 | 项目记忆系统不可用 | 输出文档索引到本地文件 | 1 | 标注"索引未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **frontend-design**(前端设计)— 生成设计系统、组件规范、页面交互说明时参考 `frontend-design`
+- **code-review**(代码审查)— 生成前可用 `code-review` 确保代码逻辑正确
+- **api-design**(API设计)— API设计完成后可用 `doc-generation` 生成接口文档
+- **code-generation**(代码生成)— 生成代码后可用 `doc-generation` 生成配套文档
+- **project-memory-management**(项目记忆管理)— 记录文档结构、模板选择理由、API 文档规范约定并更新文档索引
+```

+ 135 - 0
.claude/skills/dev-expert/references/domain-driven-design.md

@@ -0,0 +1,135 @@
+# 领域驱动设计(Domain-Driven Design)
+
+> 本文件是 `dev-expert` 的领域建模专项补充参考,不作为独立子技能计数,也不提供 `@` 显式调用入口。命中「领域建模/限界上下文/聚合根/领域事件/统一语言/防腐层/复杂业务规则建模/遗留系统隔离」等信号,且任务为复杂业务系统(多子域/术语多义/跨上下文协作)时,按需与 `software-project`、`spec-driven-development`、`architecture-decision`、`mysql-database`、`code-generation` 协同加载。CRUD 为主的脚本/营销页/小工具不加载本 reference,避免过度设计(违反 Karpathy 简洁优先)。
+
+## 适用边界
+
+- 适用:业务规则复杂、术语多义、跨子域协作的中大型系统;遗留系统渐进重构需要隔离新旧模型;需求频繁变更需要稳定领域内核抵御变化。
+- 不适用:CRUD 为主、表即业务的小工具/脚本/营销页/简单后台——直接 `mysql-database` + `code-generation` 更高效。
+- 判定信号:业务对象 ≥ 5 且存在同名异义、跨模块业务规则联动 ≥ 2、需要对接遗留系统且不想被其模型污染。
+
+## 核心概念速查
+
+| 概念                            | 定义                                             | 与现有 reference 的衔接                                       |
+| ------------------------------- | ------------------------------------------------ | ------------------------------------------------------------- |
+| 统一语言(Ubiquitous Language) | 需求→代码全程使用同一术语,消除翻译损耗          | 贯穿 `project-memory-management` Glossary,强制约束而非仅记录 |
+| 实体(Entity)                  | 有唯一身份、生命周期内身份不变的对象             | `mysql-database` 表设计前先识别实体                           |
+| 值对象(Value Object)          | 无身份、由属性值定义、不可变                     | 金额/地址/坐标等用值对象,减少表与 ORM 复杂度                 |
+| 聚合根(Aggregate Root)        | 一致性边界入口,外部只能持有根的引用             | `mysql-database` 事务边界:一个事务只改一个聚合               |
+| 领域事件(Domain Event)        | 表示领域中已发生的事实,用于解耦上下文           | `spec-driven-development` Scenario 的业务侧表达               |
+| 限界上下文(Bounded Context)   | 模型的语义边界,同一术语在不同上下文可有不同含义 | 补 `software-project` 模块边界审查的语义维度                  |
+| 上下文映射(Context Map)       | 描述上下文间关系(ACL/共享内核/客户-供应商等)   | 补 `architecture-decision` 的集成模式选择                     |
+
+## 执行流程
+
+### 第一步:建立统一语言
+
+- 从需求描述提取领域名词,与业务方确认含义,消除同名异义/异名同义
+- 术语写入 `project-memory-management` Glossary,标注所属限界上下文
+- 代码命名必须与统一语言一致(`Order` 在销售上下文 = 订单,在物流上下文 = 运单,须区分命名如 `SalesOrder`/`ShipmentOrder`)
+
+### 第二步:事件风暴识别领域事件
+
+- 用领域事件倒推业务流程:先列"什么发生了"(OrderPlaced/PaymentReceived/OrderShipped),再连成流程
+- 每个事件标注:触发者、相关实体、后续影响
+- 事件命名用过去式,表示已完成的事实
+
+### 第三步:识别聚合与聚合根
+
+- 按"一致性边界"分组:必须强一致的对象归入同一聚合,根为外部访问入口
+- 聚合设计原则:
+  - 小聚合优先:一个聚合根 + 少量值对象/内部实体,避免大聚合导致大事务
+  - 跨聚合用引用(存 ID)而非直接对象持有
+  - 聚合内业务逻辑内聚到根或内部实体(充血模型),禁止贫血模型
+  - 一个事务只修改一个聚合;跨聚合一致性用领域事件 + 最终一致
+
+### 第四步:划定限界上下文
+
+- 按语义边界切分,而非技术分层:同一术语在不同上下文可有不同模型
+- 上下文边界对齐团队/子系统/部署单元,便于独立演进
+- 输出上下文清单:上下文名 / 核心领域语言 / 对外接口 / 依赖的其他上下文
+
+### 第五步:上下文映射与集成模式选择
+
+对每对交互的上下文,选择集成模式(权衡走 `architecture-decision` 流程并附反选论证):
+
+| 模式                      | 适用场景                                      | 代价                           |
+| ------------------------- | --------------------------------------------- | ------------------------------ |
+| 防腐层(ACL)             | 接入遗留系统/外部不可控 API,避免被其模型污染 | 多一层翻译层,增加复杂度       |
+| 共享内核                  | 多上下文必须共享同一子模型                    | 共享部分变更需多方协调         |
+| 客户-供应商               | 上游(供应商)服务下游(客户),双方可协商    | 需协商机制,强势上游会压制下游 |
+| 遵奉者(Conformist)      | 下游被迫遵循上游模型                          | 下游丧失模型自主权             |
+| 各行其道(Separate Ways) | 上下文无实际耦合,独立演进                    | 可能重复建模,但解耦彻底       |
+
+### 第六步:领域模型落地实现
+
+- 聚合根作为事务与一致性入口,外部通过根的方法操作内部对象
+- 领域事件在聚合根方法内产生,由应用服务/事件分发器发布
+- 应用服务编排聚合、事务、事件发布;领域服务承载跨聚合的无状态领域逻辑
+- 仓储(Repository)按聚合根粒度提供持久化,禁止跨聚合 JOIN 式仓储
+- 与 `code-generation` 协同:生成代码须体现充血模型与聚合边界,禁止产出贫血模型 + 巨型 Service
+
+### 第七步:验证与沉淀
+
+- 领域规则用单元测试覆盖聚合根方法(不依赖数据库)
+- 跨上下文协作用集成测试覆盖事件流
+- 领域模型与术语沉淀到 `project-memory-management`,作为后续变更的基准
+
+## 架构规则
+
+1. 一个事务只修改一个聚合根;跨聚合一致性用领域事件 + 最终一致,禁止跨聚合大事务。
+2. 聚合内业务逻辑内聚到领域对象(充血模型),禁止贫血模型(只有 getter/setter 的实体)。
+3. 外部只能持有聚合根的引用,聚合内部对象不可直接被外部访问。
+4. 跨聚合引用只存 ID,禁止对象直接持有。
+5. 领域事件在领域层产生、在应用层发布,禁止领域层直接依赖消息中间件实现。
+6. 防腐层是必选项而非可选项,当对接遗留/外部系统时必须建立,禁止领域模型直接被外部模型渗透。
+7. 统一语言是硬约束:代码命名与 Glossary 不一致即视为缺陷,须在 `code-review` 阶段拦截。
+8. 新建聚合/上下文必须有真实业务驱动,禁止 speculative 建模(呼应 Karpathy 简洁优先)。
+
+## 常见坑与反模式
+
+| 场景                         | 风险                                       | 处理方式                                                  |
+| ---------------------------- | ------------------------------------------ | --------------------------------------------------------- |
+| 贫血模型 + 巨型 Service      | 业务逻辑散落、领域对象沦为 DTO             | 行为内聚到聚合根,Service 只做编排                        |
+| 跨聚合事务                   | 大事务、锁竞争、一致性边界模糊             | 拆为聚合内事务 + 领域事件最终一致                         |
+| 大聚合(一个根聚合数十实体) | 事务过大、加载性能差                       | 拆小聚合,跨聚合用 ID 引用                                |
+| 直接持有外部上下文对象       | 模型耦合、上下文边界失效                   | 只存 ID + 防腐层翻译                                      |
+| 领域事件同步阻塞发布         | 事务回滚但事件已发出、或事件失败拖垮主流程 | 事务提交后再发布(如 `DB::afterCommit` / 事务后派发)     |
+| 为 CRUD 场景强加聚合/事件    | 过度设计、违反简洁优先                     | 适用边界外场景直接走 `mysql-database` + `code-generation` |
+| 共享内核无节制扩张           | 多上下文被绑定、变更困难                   | 共享内核限制在极小稳定子集,其余走 ACL                    |
+| 统一语言仅记录不约束         | 术语漂移、模型与需求脱节                   | `code-review` 阶段强制核对命名与 Glossary 一致            |
+
+## 关联 reference
+
+| 协同点                                    | 说明                                                                                                  |
+| ----------------------------------------- | ----------------------------------------------------------------------------------------------------- |
+| `software-project` 第三步点五模块边界审查 | 本 reference 提供语义边界维度(限界上下文),与工程边界(依赖/接口/数据持有)互补;审查时先语义后工程 |
+| `mysql-database`                          | 表设计前先识别实体/值对象/聚合根;事务边界对齐聚合边界                                                |
+| `spec-driven-development`                 | Scenario 可用领域事件表达;领域模型作为 spec Design 层的核心产物                                      |
+| `architecture-decision`                   | 上下文映射集成模式选择走架构决策流程,附反选论证                                                      |
+| `code-generation`                         | 生成代码须体现充血模型与聚合边界;应用服务/领域服务/仓储分层                                          |
+| `code-review`                             | 审查贫血模型、跨聚合事务、统一语言一致性、防腐层缺失                                                  |
+| `refactoring`                             | 遗留系统重构优先建立防腐层再逐步抽取领域模型                                                          |
+| `project-memory-management`               | 统一语言写入 Glossary 并标注所属上下文;领域模型作为决策记录沉淀                                      |
+
+## 质量标准
+
+- 限界上下文必须有明确的语义边界和核心领域语言,不能按技术分层切分
+- 聚合根必须是一致性边界入口,外部不可绕过根访问内部对象
+- 跨聚合一致性必须显式选择事务或最终一致,禁止隐式跨聚合事务
+- 统一语言必须贯穿需求→代码,Glossary 与代码命名一致
+- 对接遗留/外部系统必须有防腐层,领域模型不可被外部模型直接渗透
+- 领域规则必须有单元测试覆盖(不依赖数据库)
+- 适用边界外的简单场景不得强加 DDD 模式(呼应 Karpathy 简洁优先)
+
+## 失败回退机制
+
+| 步骤                   | 失败条件                                        | 回退目标                             | 最大重试 | 不可恢复时升级路径                      |
+| ---------------------- | ----------------------------------------------- | ------------------------------------ | -------- | --------------------------------------- |
+| 第一步:建立统一语言   | 业务方术语含义不清或存在分歧                    | 列出 2-3 种理解,请业务方确认        | 2        | 标注待确认术语,暂用占位命名            |
+| 第二步:事件风暴       | 业务流程无法连成事件链                          | 退回第一步补全统一语言               | 1        | 输出流程断点清单交业务方补充            |
+| 第三步:识别聚合       | 聚合边界难以判定(强一致 vs 最终一致不明)      | 按变更频率与一致性要求分组,标注假设 | 2        | 走 `architecture-decision` 决策聚合边界 |
+| 第四步:划定限界上下文 | 上下文边界与现有模块/团队结构冲突               | 输出语义边界 vs 工程边界冲突清单     | 1        | 交架构师/业务方拍板                     |
+| 第五步:上下文映射     | 集成模式难以取舍                                | 走 `architecture-decision` 反选论证  | 1        | POC 验证后由用户拍板                    |
+| 第六步:领域模型落地   | 与现有框架/ORM 冲突(如 Eloquent 倾向贫血模型) | 输出冲突点 + 妥协方案                | 1        | 标注已知限制,部分内聚部分编排          |
+| 第七步:验证与沉淀     | 领域规则测试难以隔离数据库                      | 用内存仓储/Factory 隔离              | 2        | 标注未隔离测试项                        |

+ 104 - 0
.claude/skills/dev-expert/references/error-ledger.md

@@ -0,0 +1,104 @@
+# 判断回溯录(ERR-XXX,原「踩坑错误册」)
+
+> 任务结束后触发"新坑即录":凡本轮新踩/复现且解决耗时 ≥ 40min 的坑,强制性在索引新增一行 + 建立独立 .md。
+
+本 reference 收口于 Step 6 复盘沉淀阶段。
+
+**定位升级**:本册不只记"坑的结果"(根因/修复/防范)来省时间,更要把每一条变成**决策失败样本**——记录"当时基于什么证据判错、正确判据应是什么、为什么没想到那个信号",让诊断从"查关键词命中"升级为"见过这种错判长什么样"的模式匹配。
+
+---
+
+## 索引模板
+
+文件:`{PROJECT_ROOT}/.ai-memory/error_index.md`
+
+```markdown
+# 错误索引
+
+| ERR-ID | 一句话描述 | 根因分类 | 触发关键词 | 新增日期 | Recurrence-Count |
+| ----------- | -------------------------- | ------------ | ----------------------------------------- | ---------- | ---------------- |
+| ERR-001 | 帝国CMS路由表有值但加载失败 | 框架行为假设 | 路由表有值, 不会加载 | 2026-07-11 | 1 |
+| ERR-002 | 中文路径致Python安装失败 | 平台差异 | 用户名非英文, 安装失败 | 2026-07-12 | 1 |
+| ERR-003 | write_file时间戳副本未覆盖 | 工具行为 | 覆盖, rename-item, 时间戳副本 | 2026-07-20 | 1 |
+| ERR-004 | 环检测递归三次都判错环数 | 算法正确性 | 环检测, 递归, 回边, cycle | 2026-08-14 | 1 |
+| ERR-005 | 为简单校验过度抽象出策略模式 | 设计取舍(taste) | 过度设计, 抽象, 策略模式 | 2026-08-14 | 1 |
+| ERR-006 | build_graph 注释伪符号致假边 | 算法正确性 | 注释伪符号, 假边, strip_php_comments, phantom | 2026-08-14 | 1 |
+```
+
+**分类标准(共十类)**:框架行为假设 / 平台差异 / 配置冲突 / 缓存/竞态 / 工具行为 / 业务逻辑 / 安全沙箱 / 环境变量 / **算法正确性** / **设计取舍(taste)**。
+
+- **算法正确性**:环检测 / 图遍历 / 递归 / 状态机 / 排序等"跑通但结果错"类缺陷(如环检测三次没写对)。此前此类坑进不了册子,属分类缺口,必须单独归类。
+- **设计取舍(taste)**:取舍权衡判断失误(过度设计 vs 简化、抽象边界、API 形态、是否引入依赖等),属审美/判断力层面而非纯技术错误。
+
+---
+
+## 单条模板
+
+文件:`{PROJECT_ROOT}/.ai-memory/errors/ERR-XXX.md`
+
+```markdown
+# ERR-XXX: {错误一句话描述}
+
+- **根因分类**:
+- **触发条件**:
+- **排查路径**:
+- **错判点**:(当时基于什么证据判错了——误信了哪个信号、忽略了哪个反例)
+- **正确判据**:(应该用什么信号才对——可证伪的判定标准)
+- **盲区**:(为什么之前没想到这个信号——认知/经验缺口,而非技术盲区)
+- **修复方式(可复用)**:
+- **防范措施**:
+- **规避方案**:
+- **关联知识库**:(列出相关技术或工具的官方文档关键词 / sector)
+- **来源会话**:(可选,标记首次发生的会话 ID 或日期)
+- **Recurrence-Count**:1(复现次数,capture_learning.py 去重提示驱动累加)
+- **First-Seen**:(YYYY-MM-DD,首次发生日期)
+- **Last-Seen**:(YYYY-MM-DD,最近复现日期)
+- **Tasks**:1(跨不同任务数,Recurrence>=3 且 Tasks>=2 触发 promote 建议)
+- **Pattern-Key**:(可选,稳定去重键,如 cms.routing.table_loaded)
+```
+
+> 上方 4 个 Recurrence 字段由 capture_learning.py / recurrence_promote.py(学习捕获 hook 草案)消费:
+> 单条含此字段时,recurrence_promote.py 可评估硬规则(Recurrence-Count>=3 且 Tasks>=2 且 30 天窗口)并输出 promote 建议;缺字段则优雅跳过。
+
+禁止字段:本模板仅含技术信息。禁止记录密码 / Token / API Key / SSH Key / 账号凭证 / 生产 IP / 内部域名。
+
+---
+
+## 触发与读取
+
+### 诊断阶段触发
+
+在 Bug 诊断 / 代码审查 / PHP 内联 JS 等 session 中,Agent 优先按**错判形状**做模式匹配,而非只查触发关键词:
+
+- 先匹配 `error_index.md` 行的触发关键词,命中后读 `errors/{ERR-ID}.md` 全文
+- 重点比对单条中的 **错判点 / 正确判据 / 盲区**:当前是否落入同样的"误信信号、忽略反例"模式
+- 若当前症状与记录匹配 → 优先复用修复方式与正确判据,并对本结论先跑证伪门禁,计时从 0 开始
+- 若相似但不完全匹配 → 标记为变体,修复后新建 ERR-ID(不覆盖原条目),并在新条目的错判点中说明与旧条目的差异
+
+### 新坑即录
+
+任一 session 中踩的新坑且排查耗时超过 40 分钟(从初步定位到 fix 完成),强制性操作:
+
+1. 分配下一个可用的 ERR-ID(如 ERR-004)
+2. 按单条模板创建 `errors/{ERR-ID}.md`
+3. 在索引表追加一行
+4. 同步更新 `project_memory.md` 中的 Known Issues 节(写缩写引用:`参见 ERR-XXX`)
+5. 运行 `python hooks/recurrence_promote.py` 评估本条目及全量错误是否满足升规则硬条件(Recurrence-Count≥3 且 Tasks≥2 且 30 天窗口),满足则输出 promote 建议交用户确认(不自动改写规则文件)
+
+> **排查时间估算**:从首次发现症状到修复方案确认,按 Agent 与用户交互轮次×平均轮耗时估算;≥ 4 轮交互视为接近 40 分钟阈值,建议记录。
+
+---
+
+## 归档策略
+
+- 索引表超过 50 行时触发归档提醒:`⚠️ 错误索引已超 50 条(当前 X 条),建议将 3 个月前的旧条目归档至 archive/error_index_{YYYY}.md`
+- 单条 ERR-XXX.md 超过 500 行时触发拆分提醒
+- 合并规则:连续 3 个 ERR-ID 同一根因 → 合并为一条,保留各变体修复方式
+
+---
+
+## 禁忌
+
+- 不记录密码 / Token / API Key / 凭据 / 生产 IP
+- 不经确认不写入
+- 不跨项目复用(每项目独立索引)

+ 196 - 0
.claude/skills/dev-expert/references/execution-safety.md

@@ -0,0 +1,196 @@
+# 执行安全:审计、防线、清单、确认
+
+本 reference 覆盖执行阶段的安全保障机制,收口于 Step 2(规划门禁)、Step 3(执行)。
+引用以下机制(顺序对应执行流程):
+
+---
+
+## 规划门禁(PLAN-GATE,Step 2 启用)
+
+复杂任务在进入修改阶段前必须通过。Trivial Fix(单文件 ≤ 1 行,无结构变更;或 ≤3 文件且每文件 ≤5 行、无结构变更)可跳过;虽通过但触发异常 → 强制中止。
+
+```
+☐ 1. 是否已遍历所有用户定制 / 人工干预文件?
+☐ 2. 是否已覆盖所有相关依赖模块?
+☐ 3. 外部系统 / 接口影响已评估?
+☐ 4. 新引入库的许可证兼容性已确认?
+☐ 5. 命令行 / 脚本参数完整无硬编码路径?
+☐ 6. 数据库迁移回滚方案已就绪?
+☐ 7. 测试集已选定或确认无需新测试?
+☐ 8. 性能敏感路径已标注(需基准对比)?
+☐ 9. 批量范围已确认(文件数 / 记录数 / 受影响 API 数)?
+☐ 10. 回滚 / 恢复机制已定义?
+☐ 11. 安全漏洞扫描已预判(SQL注入/XSS/路径遍历/信息泄露)?
+☐ 12. 并发 / 竞态 / 幂等场景已考虑?
+☐ 13. 方案已回读确认格式无乱码/截断?
+☐ 14. 自动跳过项已注原因?
+☐ 15. 中断点与恢复路径已标注?
+```
+
+---
+
+## 写码前实码确认
+
+修改或生成代码前,必须完成:
+
+1. **读文件**:`read_file` 目标文件,确认上下文、编码、插入位置
+2. **搜索关联**:`search_content` 在项目中搜关键函数名/类名/表名,确认无遗漏引用
+3. **冲突检查**:确认当前文件无正在进行中的未合并变更
+
+三项均完成后才允许进入修改操作。跳过任一项 → 退回 Step 2。
+
+---
+
+## 审计修复分离
+
+适用于代码审查、Bug 诊断、重构建议等场景:
+
+- **审计阶段**:只读操作,收集证据(问题清单、堆栈、日志、复现步骤),禁止修改任何文件
+- **修复阶段**:审计阶段结束后,输出结构化诊断结论,经确认后才进入修改
+
+两阶段不可交叉。审计未完成前改代码 → 视为未完成审计,退回重做。
+
+审计输出格式(修复阶段的前置输入):
+
+```
+## 审计结论
+- 问题定位: {文件:行号}
+- 根因分类: {框架行为/配置/缓存/竞态/逻辑错误/...}
+- 影响范围: {受影响模块/功能}
+- 修复方向: {最小改动方案}
+- 验证方式: {如何确认修复有效}
+```
+
+---
+
+## 不卡死计数器
+
+同一诊断点在当前 session 内连续失败 ≥ 3 次 → 触发:
+
+1. 暂停当前方向的修复尝试
+2. 列出 2-3 条替代方案(不同策略,非参数微调)
+3. 说明各方案的成本、风险与验证方式
+4. 交用户选择后继续
+
+禁止行为:同参数微调绕过计数器;换一句等价表述重试同一思路。
+
+---
+
+## 澄清策略分级(高 / 中 / 低,Step 2 引用)
+
+SKILL.md Step 2 要求「业务规则 / 验收口径不明时,按澄清策略分级决定是否补问」。本节能级定义,执行时据此判定**问不问、问多少**,消除此前「分级」仅被引用无定义导致的决策点悬空。
+
+### 三级判定标准
+
+| 级别 | 触发条件(满足任一即归入该级) | 执行动作 |
+| - | - | - |
+| **高级** | ① 业务规则不明且错判有业务后果(如默认渠道 / 默认状态 / 计费口径写错会出脏数据);② 涉及生产配置 / 数据库写入 / 权限 / 凭证 / 不可回滚操作;③ 多利益方视角未区分优先级;④ 验收口径直接决定"做对了没"却未给出 | **先问关键问题再执行**,最多 3 问;不假设、不默认。用户未回前不进入修改。**降级例外(仅限非 DDL 的代码改动)**:若该操作具备可靠回滚点(`.bak` / 事务 / 版本化,**且环境已启用 `.bak` 自动备份 hook**)且影响可局部化,由高级降为**中级**——给默认假设 + 显式标注"如不符合请纠正",允许先执行后纠,不阻塞(仍保留 SELF-AUDIT / 回滚兜底)。**破坏性操作(DDL / 删除 / 覆盖 / 批量写)一律不降级**,强制走 `delivery-assurance.md`「确认超时默认规则」的确认流,优先级高于本降级例外 |
+| **中级** | ① 有合理默认但不同默认会导致实现差异(如返回 `0` / `0.0` / 抛异常、分页用游标 vs offset);② 范围边界(是否连带改调用方)未确认;③ 性能 / 兼容性约束可推断但需对齐 | **先提案再纠**:给带权衡的推荐方案——列出候选方案 + 各自 trade-off + 推荐项 + 明确假设标注("如不符合请纠正"),用户可在交付前纠正;不阻塞执行。仅对无候选方案的低歧义点给单一默认值 |
+| **低级** | ① 纯信息查询 / 只读评估;② 目标明确、范围小、风险低、有最小验证闭环;③ 口径可无歧义从上下文推出 | **直接假设并继续**,无需单列确认;交付时说明假设即可 |
+
+### 分级判定顺序
+
+1. 先判**高级**:命中任意高级条件 → 进高级。
+2. **高级降级检查**:高级命中且满足「**非 DDL 的代码改动** + 可逆(有 `.bak` / 事务 / 版本化回滚,且环境已启用 `.bak` 自动备份 hook)+ 影响局部化」→ **降为中级**(给默认 + 标注可纠正,不阻塞);命中 DDL / 删除 / 覆盖 / 批量写 / 不可逆 / 影响全局 / 涉生产配置 / 涉凭证隐私 → 维持高级硬卡,并转 `delivery-assurance.md` 确认超时规则处理。
+3. 否则判**中级**:命中任意中级条件 → 进中级,**先提案再纠**(带 trade-off 的推荐方案 + 假设标注)。
+4. 否则 → **低级**:直接执行。
+
+### 与意图三分法的关系
+
+本分级只决定「澄清动作强度」,不替代意图三分法(信息查询 / 简单任务 / 复杂任务)对执行模式的判定。典型组合:
+- 高级 + 复杂任务 → 先澄清后方案,双门槛。
+- 中级 + 简单任务 → 给默认标注后快速通道执行。
+- 低级 + 信息查询 → 直接回答,不写文件。
+
+### 反模式(真实执行易踩)
+
+- **澄清过度**:低级任务却反复追问 → 触发对话流异常边界「澄清过度」,进快速通道。
+- **澄清不足**:高级条件已命中却未确认即执行 → 触发「澄清不足」,下调自主度补问。
+- **伪分级**:把"想偷懒不问"包装成低级 → 凡涉库 / 权限 / 生产配置一律不得降为低级,安全闸门优先。
+
+---
+
+## 批量修改 7 防线
+
+批量修改(同操作影响 ≥ 3 文件或 ≥ 10 处修改点)时启用:
+
+| 防线 | 动作 | 失败处置 |
+| - | - | - |
+| 1. 预检 | 全局 `lint`(含 PHPStan/ESLint 等),确保基线干净 | FAIL → 修复后重跑 |
+| 2. 试点 | 取 3-5 个代表性文件,试改 | 任意 FAIL → 调整规则后重试,上限 2 次 |
+| 3. 备份 | 改前 `cp → .bak`(保留最近 3 个历史版本 `.bak` / `.bak.1` / `.bak.2`),范围对齐批量计划 | 不可跳过 |
+| 4. 批量执行 | 按计划逐文件执行 | 输出 `[OK/SKIP/ROLLBACK]` 逐文件日志 |
+| 5. 后检 | 全局 `lint`,与前基线对比 | 新增问题 → 回退 |
+| 6. MD5 校验 | 预期外变更文件输出 MD5 对比 | 标注差异来源 |
+| 7. 回读确认 | 抽查 3-5 个文件,确认修改点正确 | 不一致 → 整批回退 |
+
+回退规则:`ROLLBACK` 总量 > 20% → 整批回退。回退方式:倒序 `.bak` 恢复(`.bak` 为最新备份,`.bak.1` 次之,`.bak.2` 最旧)。
+
+---
+
+## 清单化质量自审
+
+代码交付前逐条自审(收口于 SELF-AUDIT § 代码质量):
+
+```
+☐ 异常处理完备(try-catch / 错误返回 / 边界条件)
+☐ 硬编码常量已提取
+☐ SQL / 外部输入已参数化
+☐ 输出已转义(XSS / 模板引擎)
+☐ 敏感信息不入日志/返回值
+☐ 文件操作含路径白名单校验
+☐ 超时/重试已设置上限
+☐ 并发/竞态场景已处理
+☐ 接口幂等性已保证(写操作)
+☐ 注释与代码一致(无过期注释)
+```
+
+---
+
+## 安全红线清单
+
+文件操作、数据访问、用户输入处理等场景必须逐条对照:
+
+```
+☐ SQL 参数化(无字符串拼接用户输入)
+☐ 输出转义(HTML / JS / URL 上下文)
+☐ 文件路径白名单 / 拒绝 ../ 穿越
+☐ 上传类型白名单 + 大小限制
+☐ 敏感信息不入前端(密钥/Token/内部路径)
+☐ CSRF Token(状态变更操作)
+☐ 权限校验后端兜底(前端校验仅为 UX)
+☐ 错误信息不泄露内部细节
+☐ 第三方依赖版本已知漏洞已排查
+☐ 日志不含密码/Token/身份证号
+```
+
+---
+
+## session_memory 标识(id)生成规则(Step 0 / Step 6 引用)
+
+SKILL.md Step 1.1 Layer 2(L86 区)读取 `{YYYYMMDD}/session_memory_{session-id}.jsonl`、Step 6(L240)写入同名文件;首次会话无 session-id 时由 Step 0 探测阶段生成(见下生成规则),兜底读取取当日最大时间戳 jsonl。为避免跨会话恢复时找不到对应文件,本节能级统一 **`session-id` 的生成与存储约定**:
+
+### 生成规则
+
+- **格式**:`session-id` = `YYYYMMDD-HHmm`。
+- **生成时机**:本次会话首次进入 **Step 0(新会话加载 / 探测阶段)** 即生成并写入当日 `daily.md` 顶部 `session-id: {YYYYMMDD-HHmm}` 行(唯一真相源);若 daily.md 无此行(旧会话 / 首次),取当日目录下时间戳最大的 `session_memory_*.jsonl` 兜底(见下读取规则)。
+- **存储**:写进当日 `daily.md` 顶部一行 `session-id: {YYYYMMDD-HHmm}`,作为唯一真相源。
+- **读取**:Step 0 恢复时,先读 `{YYYYMMDD}/daily.md` 顶部的 `session-id` 行,再据该值定位 `session_memory_{session-id}.jsonl`;若 daily.md 无此行(旧会话 / 首次),则取当日目录下文件名时间戳最大的 `session_memory_*.jsonl`。
+
+### 收敛要点
+
+- 单日多会话:各自有独立 `session-id`,互不覆盖。
+- 续做定位:用户说"继续"时,按 `daily.md` 记录的 `session-id` 精确恢复,不依赖猜测文件名。
+- 无历史:探测失败 → 正常进入意图识别,不阻塞(与 Step 1.1 触发规则一致)。
+
+---
+
+## 对接点
+
+| 文件 | Step | 说明 |
+| - | - | - |
+| `delivery-assurance.md` | Step 5 | SELF-AUDIT 引用质量自审清单与安全红线清单 |
+| `error-ledger.md` | Step 6 | 踩坑若涉及批量/安全/质量模式,新坑即录 |
+| `software-project.md` | Step 3/Step 5 | 项目总控的验收引用交付检查清单 |
+| `code-generation.md` | 第〇步 | 复杂代码生成前判定需否安全红线预检 |
+| `SKILL.md` | Step 2 / Step 0 / Step 6 | 澄清策略分级定义、session-id 生成规则(消悬空引用) |

+ 436 - 0
.claude/skills/dev-expert/references/frontend-design.md

@@ -0,0 +1,436 @@
+# 前端设计
+
+面向 Web 前端、H5、后台管理、官网、落地页、CMS 模板页面、品牌视觉、Banner、图标和社交媒体图片,提供从需求理解、设计思维、信息架构、视觉设计、交互状态、响应式布局到工程落地和浏览器验证的完整流程。
+
+> 本模板已吸收 UI/UX Pro Max 规则,无外部依赖。设计优先级:无障碍 > 响应式 > 视觉层级 > 一致性 > 性能 > 交互反馈 > 排版 > 色彩 > 动效 > 间距(具体要求见后续步骤)。
+
+## 输入要求
+
+1. **页面/功能目标**(必填):页面要解决什么用户问题或业务目标
+2. **目标用户**(必填):使用者角色、设备场景、操作频率
+3. **内容素材**(可选):文案、图片、品牌色、Logo、现有页面
+4. **技术栈**(可选):HTML/CSS/JS、Vue、React、Taro、CMS模板等
+5. **设计约束**(可选):品牌规范、兼容浏览器、SEO、性能、无障碍要求
+6. **设计资产类型**(可选):页面、组件库、品牌规范、Banner、图标、社媒图、数据图表
+
+## 执行流程
+
+### 第一步:需求与场景分析
+
+- 明确页面核心目标和转化路径
+- 识别用户角色、设备、访问入口和关键任务
+- 明确交付类型:官网/后台/H5/表单/列表/详情/仪表盘/CMS模板/品牌规范/Banner/图标/社媒图
+- 标注不确定信息,必要时先向用户确认
+
+```
+✓ 前端场景分析:
+- 页面类型: [类型]
+- 设计资产: [页面/品牌/Banner/图标/社媒图]
+- 目标用户: [角色]
+- 核心任务: [任务]
+- 主要设备: [桌面/移动/自适应]
+```
+
+### 第二步:设计思维与 UX 策略
+
+- 使用双钻模型:发现问题 → 定义问题 → 构思方案 → 交付方案
+- 必要时输出用户画像、用户旅程、痛点、HMW 问题陈述
+- 营销页可使用 AIDA:注意 → 兴趣 → 欲望 → 行动
+- 数据页必须选择合适图表:柱状/折线/漏斗/热力/雷达/树图等,并说明理由
+- 明确首屏价值主张、主 CTA、用户三次点击内的目标路径
+
+### 第二步点五:快速原型与草图
+
+在进入完整信息架构前,对关键页面输出低保真原型,快速对齐视觉预期,减少后期返工。
+
+**适用条件**(满足任一即启用):
+
+- 首次设计该页面/组件,无现存参考
+- 用户需求描述少于 30 字且无设计参考图
+- 涉及多步骤交互流程
+
+**输出内容**:
+
+1. **线框图**:用 ASCII 或 Markdown 表格绘制关键页面的区块布局,标注每个区块的内容和优先级
+2. **交互流**:用箭头文本描述关键路径的页面跳转和状态变化
+3. **关键决策点**:标注哪些设计选择需要用户确认(如导航方式、列表 vs 卡片、弹窗 vs 新页面)
+
+**输出格式**:
+
+```
+┌─────────────────────────────┐
+│  Header (Logo + 导航)       │  ← P0
+├─────────────────────────────┤
+│  Hero / 核心操作区          │  ← P0
+├─────────────────────────────┤
+│  内容区 (列表/卡片/表单)    │  ← P1
+├─────────────────────────────┤
+│  Footer                     │  ← P2
+└─────────────────────────────┘
+
+交互流: 列表页 → 点击[新建] → 表单页 → 提交 → 列表页(刷新+提示)
+待确认: 列表用表格还是卡片?筛选放顶部还是侧栏?
+```
+
+此步骤产出未经视觉润色,仅用于结构对齐。用户确认骨架后进入第三步细化。
+
+### 第三步:信息架构与内容层级
+
+- 梳理页面区块:Header / Hero / 内容区 / CTA / Footer 等
+- 明确信息优先级:首屏必须传达什么,次级内容如何展开
+- 定义导航、面包屑、筛选、分页、标签等结构
+- 对 CMS 页面,明确模板变量和字段来源
+- 对 Banner/社媒图,明确主标题、副标题、CTA、Logo、背景、视觉焦点和安全区
+
+### 第四步:视觉风格、品牌与设计系统
+
+定义可落地的设计系统,而不是只描述“好看”:
+
+| 维度      | 产出                                               |
+| --------- | -------------------------------------------------- |
+| 设计风格  | 风格名称、适用场景、不适用场景、关键视觉特征       |
+| 色彩      | 主色、辅助色、背景色、状态色、文本色、暗色模式覆盖 |
+| 字体      | 字体配对、字号层级、行高、字重、标题/正文/注释     |
+| 间距      | 4/8px 栅格或项目现有 spacing token,组件/分区间距  |
+| 圆角/阴影 | 卡片、按钮、浮层、输入框规范                       |
+| 图标/图片 | 图标风格、图片比例、alt、占位、压缩和导出策略      |
+| 组件      | Button/Input/Card/Table/Dialog/Toast 等            |
+| 品牌      | Logo 安全距离、最小尺寸、禁用规则、品牌语气        |
+
+设计系统必须采用三级 token 思路:
+
+```css
+/* 原始 Token */
+--color-blue-600: #2563eb;
+--space-4: 1rem;
+
+/* 语义 Token */
+--color-primary: var(--color-blue-600);
+--spacing-section: var(--space-12);
+
+/* 组件 Token */
+--button-bg: var(--color-primary);
+--card-padding: var(--space-6);
+```
+
+#### CSS/SCSS 工程约束
+
+| 规则       | 要求                                                                                                                  |
+| :--------- | :-------------------------------------------------------------------------------------------------------------------- |
+| 嵌套深度   | ≤3 层,禁止过度嵌套导致特异性膨胀                                                                                     |
+| z-index    | 统一管理,定义层级常量:`--z-dropdown:100` / `--z-sticky:200` / `--z-overlay:300` / `--z-modal:400` / `--z-toast:500` |
+| 单位优先级 | `rem` > `vh/vw/%` > `px`;字体/间距走 rem,布局走百分比或 vw                                                          |
+| 公共样式   | 变量/mixin/函数抽离到 `variables.scss`/`mixins.scss`,组件内禁止重复定义                                              |
+| 行内样式   | 禁止;特殊情况需注释说明理由                                                                                          |
+| 硬编码颜色 | 禁止;必须走 CSS 变量或设计 token                                                                                     |
+
+### 第五步:组件、交互状态与资产规格
+
+每个可交互元素必须覆盖状态:
+
+- 默认 / Hover / Focus / Active / Disabled
+- Loading / Empty / Error / Success
+- 表单校验错误、异步提交、防重复提交
+- 弹窗、抽屉、提示、确认二次操作
+- 后台管理必须考虑批量操作、筛选重置、分页保留
+- 长任务必须设计 AJAX 渐进式防卡死交互:Init 创建任务、Step 分批执行、Poll 轮询状态
+- Banner 必须定义尺寸、主视觉、主标题、副标题、CTA、Logo 和安全区
+- 图标必须定义尺寸、线宽、圆角、填充/描边、SVG title/aria 策略
+- 社媒图必须定义平台尺寸、文字安全区、导出格式和品牌一致性
+
+#### Init-Step-Poll 前端交互规范
+
+| 阶段      | 前端动作                               | UI 状态                        |
+| --------- | -------------------------------------- | ------------------------------ |
+| Init      | 提交参数,禁用按钮,创建任务           | `queued`,显示初始化中         |
+| Step      | 循环请求或由 Poll 驱动分批执行         | `running`,显示进度条/当前批次 |
+| Poll      | 轮询任务状态,不重复提交表单           | 显示百分比、失败数、日志摘要   |
+| Done      | 停止轮询,展示结果和下一步操作         | `success`,允许下载/查看/刷新  |
+| Failed    | 停止轮询,展示错误和重试入口           | `error`,允许重试/复制错误     |
+| Cancelled | 停止轮询,展示已取消状态和重新发起入口 | `cancelled`,允许重新发起/返回 |
+
+**交互约束**:
+
+- 轮询间隔建议 800-2000ms,页面隐藏时降频或暂停
+- 连续失败 3 次必须停止轮询并提示网络/服务异常
+- 用户离开页面前,如任务仍在运行,应提示任务可在后台继续或确认取消
+- 禁止用户重复点击造成重复任务;按钮必须有 disabled/loading 状态
+- 进度必须可感知:百分比、已处理/总数、当前步骤、失败数至少提供两项
+- 取消后状态变为 `cancelled`,必须停止轮询并禁止继续提交 Step / Poll;允许重新发起或返回
+
+### 第六步:响应式与兼容性设计
+
+| 断点         | 设计重点                                          |
+| ------------ | ------------------------------------------------- |
+| `sm` 640px   | 移动端单列布局、触控热区不小于 44px、底部操作优先 |
+| `md` 768px   | 平板双列/折叠侧栏、表格横向滚动                   |
+| `lg` 1024px  | 桌面信息密度、快捷操作、固定筛选/侧栏             |
+| `xl` 1280px  | 大屏内容宽度控制,避免行长过宽                    |
+| `2xl` 1536px | 宽屏留白、网格扩展、数据看板布局                  |
+
+兼容性要求:
+
+- 避免依赖实验性 CSS,除非明确允许
+- 表格、长文本、图片必须定义溢出策略
+- CMS 模板需兼容目标站点现有 CSS 作用域,避免全局污染
+
+### 第七步:可访问性、安全性与可用性检查
+
+#### 可访问性
+
+- 颜色对比度至少达到 WCAG AA
+- 表单控件必须有 label 或 aria-label
+- 键盘可访问:Tab 顺序、Focus 样式、Esc 关闭浮层
+- 图片必须有 alt;装饰图可用空 alt
+- 动画必须可降级,避免强闪烁和过度动效
+- 交互反馈需在 100ms 内可感知,长任务必须有 Loading 或进度提示
+- 空状态不得为空,必须提供说明、引导或 CTA
+
+#### 前端安全
+
+| 威胁       | 防护措施                                                                                            |
+| :--------- | :-------------------------------------------------------------------------------------------------- |
+| XSS        | 用户输入输出必须 `htmlspecialchars()` / `v-text` / `dangerouslySetInnerHTML` 审查;富文本白名单过滤 |
+| CSRF       | 状态变更请求必须带 Token;Cookie `SameSite=Strict`                                                  |
+| 敏感信息   | 禁止明文存储密钥/Token 于前端代码或 localStorage;敏感数据仅存 HttpOnly Cookie                      |
+| 接口权限   | 前端路由守卫 + 后端接口鉴权,禁止仅靠前端隐藏按钮                                                   |
+| 第三方脚本 | CDN 资源必须 `integrity` + `crossorigin`,禁用不可信第三方 JS                                       |
+
+### 第八步:前端实现方案
+
+根据项目技术栈输出可执行实现方案:
+
+- HTML 语义结构
+- CSS 命名与作用域策略(BEM/CSS Modules/Scoped CSS/Tailwind/项目现有规范)
+- 组件拆分:页面组件、业务组件、基础组件
+- 状态管理:本地状态、URL查询参数、全局状态
+- 数据接口:字段映射、空值处理、错误提示
+- CMS模板:模板标签、字段转义、缓存清理、静态资源路径
+- 长任务交互:Init/Step/Poll 接口字段、轮询间隔、取消/重试策略
+- 组件库选择:按项目技术栈选择原生、Tailwind、shadcn/ui、Ant Design、Element Plus 等,并说明取舍
+- 品牌/Banner/图标/社媒图:输出 HTML/CSS/SVG 或设计参数,不把设计资产停留在文字描述
+
+#### 项目目录规范
+
+通用前端项目结构(按技术栈调整):
+
+```
+src/
+├── api/          # 接口请求层,按模块拆分
+├── assets/       # 静态资源(图片/字体/图标)
+├── components/   # 公共组件
+├── composables/  # 组合式函数(Vue)/ 自定义 Hooks(React)
+├── constants/    # 常量与枚举
+├── hooks/        # 业务 Hooks
+├── layouts/      # 布局组件
+├── pages/        # 页面组件
+├── router/       # 路由配置
+├── store/        # 状态管理
+├── styles/       # 全局样式、变量、mixin
+├── types/        # TypeScript 类型定义
+├── utils/        # 工具函数
+└── App.vue / main.ts
+```
+
+#### 命名规范
+
+| 类别           | 规则               | 示例                               |
+| :------------- | :----------------- | :--------------------------------- |
+| 组件文件       | 大驼峰             | `UserList.vue`、`OrderDetail.tsx`  |
+| 工具/页面/样式 | 短横线             | `user-info.ts`、`index.scss`       |
+| 变量           | 小驼峰             | `userList`、`isLoading`            |
+| 常量           | 全大写+下划线      | `MAX_COUNT`、`API_BASE_URL`        |
+| 布尔           | is/has/should 前缀 | `isVisible`、`hasPermission`       |
+| 函数           | 动词开头           | `getUserInfo()`、`handleSubmit()`  |
+| 事件处理       | handle 前缀        | `handleClick`、`handleChange`      |
+| CSS 类名       | 短横线 / BEM       | `user-card`、`card__title--active` |
+
+#### 代码质量基线
+
+| 语言           | 规则                                                                             |
+| :------------- | :------------------------------------------------------------------------------- |
+| **通用**       | const/let 禁止 var;禁止魔法数字→抽常量;嵌套 ≤3 层;早返回消除 else             |
+| **JavaScript** | 箭头函数优先;async/await 优先;数组用 map/filter/reduce                         |
+| **TypeScript** | 必须定义接口/类型;禁止 any→用 unknown;函数参数+返回值必标注类型                |
+| **Vue**        | 组件名多单词;props 定义 type/default/validator;computed 禁止副作用;watch 慎用 |
+| **React**      | 函数式+Hooks;禁止循环/条件中使用 Hooks;状态扁平化;事件 handle 开头            |
+
+#### ESLint / Prettier 基线配置
+
+| 规则       | 值                 |
+| :--------- | :----------------- |
+| 缩进       | 2 空格             |
+| 引号       | 单引号             |
+| 分号       | 强制末尾分号       |
+| 文件末尾   | 保留一个空行       |
+| 未使用变量 | error              |
+| console    | warn(生产 error) |
+| 尾逗号     | es5 模式           |
+| 行宽       | 100 字符           |
+
+#### 性能实现规范
+
+| 类别      | 规则                                                                            |
+| :-------- | :------------------------------------------------------------------------------ |
+| 图片      | 压缩 + WebP 格式;`loading="lazy"` 懒加载;响应式 `srcset`                      |
+| 组件/路由 | 路由懒加载 `() => import()`;非首屏组件异步加载                                 |
+| 高频事件  | scroll/resize/input 必须防抖(300ms)或节流(100ms)                            |
+| 大列表    | >100 条必须虚拟滚动,禁止全量渲染 DOM                                           |
+| 渲染优化  | `React.memo`/`useMemo`/`computed` 减少冗余渲染;避免在 render 中创建新对象/函数 |
+| 首屏      | 关键 CSS 内联,非关键资源 `async/defer`;控制首屏请求数                         |
+
+### 第九步:浏览器验证
+
+前端交付必须有验证证据:
+
+| 验证项   | 方法                                                 |
+| -------- | ---------------------------------------------------- |
+| 视觉还原 | 浏览器截图或人工对照                                 |
+| 响应式   | 检查移动/平板/桌面断点                               |
+| 交互状态 | 点击、输入、提交、错误、空状态                       |
+| 控制台   | 无 JS error / 资源 404                               |
+| 网络     | 核心接口状态码正确                                   |
+| 性能     | 首屏资源不过大,图片尺寸合理                         |
+| A11y     | 对比度、键盘导航、aria/label                         |
+| 设计资产 | 尺寸、导出格式、安全区、品牌一致性                   |
+| 安全     | XSS 过滤、CSRF Token、敏感信息不入前端代码、接口权限 |
+| 长任务   | Init/Step/Poll 状态流正确,轮询可停止、可失败提示    |
+
+### 第十步:记录到项目记忆
+
+前端设计完成后,将关键决策记录到项目记忆(参见 `project-memory-management.md`):
+
+- 记录设计系统 token、字体配对、色板、组件约定和断点规范
+- 记录页面结构、交互模式、设计风格和可访问性要求
+- 记录品牌规范、Banner尺寸、图标风格、社媒图规格
+- 记录 CMS 模板变量、字段映射和静态资源路径约定
+
+## 国际化(i18n)规范
+
+> 当项目需要支持多语言、多区域或跨时区时启用本节。
+
+**启用条件**:项目要求 ≥2 种语言 / 同界面需按地区展示不同格式 / API 提示需按客户端语言切换 / 后台需多语言内容管理。
+
+**核心规则**:
+
+- 所有用户可见文本外置到语言包,不硬编码在组件内
+- 语言包键使用 `模块.场景.键` 命名,每个键附注释说明出现位置
+- 占位符用插值变量,复数用 ICU MessageFormat,不写三目表达式
+- 项目指定 fallback 语言,缺失键时回落到默认语言而非显示键名
+- 日期/数字/货币/百分比使用 `Intl.*Format`,时区统一存 UTC 展示时转换
+- 语言包结构:`locales/{locale}.json` + `index.js` 加载器按 locale 异步加载
+
+**API 多语言约定**:客户端通过 `Accept-Language` 头或 URL 前缀传递语言;服务端按请求语言返回错误消息和枚举文案;业务枚举值保持语言无关,文案由前端按 locale 映射;API 返回 ISO 8601 时间戳和原始数字,格式化由前端处理。
+
+**验证要点**:所有可见文本已外置 / 复数占位符使用 ICU 规则 / 缺失键有 fallback / 至少完成 2 种语言完整覆盖校验。
+
+## 输出格式
+
+````markdown
+## 前端设计方案
+
+### 1. 页面目标
+
+- **页面类型**: [官网/后台/H5/CMS模板]
+- **设计资产**: [页面/品牌/Banner/图标/社媒图]
+- **目标用户**: [用户角色]
+- **核心任务**: [一句话说明]
+
+### 2. UX策略
+
+- **设计模型**: [双钻/AIDA/用户旅程/其他]
+- **首屏价值主张**: [说明]
+- **主CTA**: [说明]
+
+### 3. 信息架构
+
+| 区块     | 内容   | 优先级   | 说明   |
+| -------- | ------ | -------- | ------ |
+| [区块名] | [内容] | P0/P1/P2 | [说明] |
+
+### 4. 视觉与品牌规范
+
+| 项目          | 值                  | 用途       |
+| ------------- | ------------------- | ---------- |
+| 设计风格      | [风格]              | [适用场景] |
+| Primary Color | [颜色/token]        | [用途]     |
+| 字体配对      | [标题/正文]         | [用途]     |
+| Logo规则      | [安全距离/最小尺寸] | [说明]     |
+
+### 5. 组件与状态
+
+| 组件   | 状态                           | 行为   |
+| ------ | ------------------------------ | ------ |
+| Button | default/hover/loading/disabled | [行为] |
+
+### 6. 设计资产规格
+
+| 资产   | 尺寸/格式 | 安全区 | 导出要求 |
+| ------ | --------- | ------ | -------- |
+| Banner | [尺寸]    | [说明] | [格式]   |
+| Icon   | [尺寸]    | [说明] | SVG/PNG  |
+
+### 7. 响应式策略
+
+| 断点   | 布局   | 注意事项 |
+| ------ | ------ | -------- |
+| mobile | [布局] | [说明]   |
+
+### 8. 实现说明
+
+```[语言/框架]
+[关键结构或组件代码]
+```
+
+### 9. 验证清单
+
+| 检查项     | 状态        | 证据        |
+| ---------- | ----------- | ----------- |
+| 响应式     | 通过/未通过 | [截图/步骤] |
+| 控制台错误 | 通过/未通过 | [结果]      |
+| 可访问性   | 通过/未通过 | [结果]      |
+| 安全性     | 通过/未通过 | [结果]      |
+````
+
+## 质量标准
+
+- 不得只给抽象审美描述,必须给出可执行的布局、组件、状态和 token
+- 必须说明设计风格、色彩、字体、间距、品牌和组件规则
+- 品牌/Banner/图标/社媒图请求必须输出尺寸、导出格式、安全区和验收标准
+- 关键交互必须覆盖 Loading/Empty/Error/Disabled 状态
+- 长任务必须采用 Init → Step → Poll,前端必须提供进度、错误、重试/取消和防重复提交
+- 移动端触控热区不得小于 44px
+- 表单必须包含校验规则和错误提示
+- 输出到页面的用户内容必须转义,CMS 模板必须避免 XSS
+- 图片必须有尺寸、比例、alt 和降级策略
+- 必须说明响应式断点和溢出处理
+- 前端交付必须包含浏览器验证方法或证据
+- CSS 嵌套深度 ≤3,z-index 必须统一管理,颜色禁止硬编码
+- 前端安全必须逐项检查:XSS 过滤、CSRF Token、敏感信息不入 localStorage、接口权限后端兜底
+- 命名遵循规范:组件大驼峰、工具短横线、布尔 is/has 前缀、函数动词开头
+- TypeScript 禁用 any,函数签名必须完整标注类型
+- ESLint/Prettier 基线必须输出 2空格/单引号/分号/末尾空行四项
+
+## 失败回退机制
+
+| 步骤                                 | 失败条件                                     | 回退目标                                   | 最大重试 | 不可恢复时升级路径                     |
+| ------------------------------------ | -------------------------------------------- | ------------------------------------------ | -------- | -------------------------------------- |
+| 第一步:需求与场景分析               | 页面目标或用户角色不清                       | 列出2-3种页面/资产类型假设,由用户选择     | 2        | 输出澄清问题清单,等待用户确认         |
+| 第二步:设计思维与 UX 策略           | 用户路径或转化目标不清                       | 使用最小用户旅程和 AIDA 草案               | 1        | 输出 UX 假设清单                       |
+| 第三步:信息架构与内容层级           | 内容素材不足                                 | 生成结构骨架并标注待补文案/图片            | 1        | 输出素材缺口清单                       |
+| 第四步:视觉风格、品牌与设计系统     | 无品牌规范                                   | 使用中性设计系统,标注可替换 token         | 1        | 要求用户提供品牌色/参考站              |
+| 第五步:组件、交互状态与资产规格     | 业务流程或资产规格不完整                     | 覆盖通用状态和标准尺寸,复杂状态标注待确认 | 1        | 输出状态机/资产规格草案                |
+| 第六步:响应式与兼容性设计           | 目标设备不明确                               | 默认移动优先 + 桌面增强                    | 1        | 标注兼容范围假设                       |
+| 第七步:可访问性、安全性与可用性检查 | 无法测量对比度、安全基线缺失或键盘行为不可用 | 输出检查清单和手动验证步骤                 | 1        | 标注未实测项                           |
+| 第八步:前端实现方案                 | 技术栈未知                                   | 输出 HTML/CSS 通用方案,避免框架特定代码   | 0        | 要求用户确认技术栈                     |
+| 第九步:浏览器验证                   | 无运行环境                                   | 输出本地验证步骤,标注未截图               | 1        | 等待用户提供运行地址或截图             |
+| 第十步:记录到项目记忆               | 项目记忆系统不可用                           | 输出前端设计决策到本地文件                 | 1        | 标注"设计规范未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **code-generation**(代码生成)— 根据前端设计方案生成页面结构、样式和组件代码
+- **code-review**(代码审查)— 审查前端实现的可访问性、性能、安全和状态覆盖
+- **test-generation**(测试用例生成)— 生成交互、表单、响应式和组件测试
+- **doc-generation**(文档生成)— 将设计系统和组件规范整理为文档
+- **cms-development**(CMS二次开发)— CMS模板页面参考其模板变量、输出转义和缓存规范
+- **project-memory-management**(项目记忆管理)— 记录设计 token、组件约定和断点规范

+ 145 - 0
.claude/skills/dev-expert/references/java-development.md

@@ -0,0 +1,145 @@
+# Java 专项开发
+
+> 本文件是 `dev-expert` 的 Java/Spring 专项补充参考,不作为独立子技能计数,也不提供 `@` 显式调用入口。命中 Java、Spring Boot、Spring、MyBatis、Hibernate、JPA、Maven、Gradle、JUnit、Mockito、JVM、GC、线程池、并发、Java 源码分析等信号时,按需与 `code-generation`、`code-review`、`test-generation`、`performance-benchmark`、`software-project` 协同加载。
+
+## 适用边界
+
+- 适用:Java 项目源码走读、Spring Boot 应用开发、接口设计、数据访问、单元测试、并发分析、JVM/GC 基础诊断。
+- 不适用:未确认语言/框架时强行套用 Spring 结构;Android、纯前端、PHP/CMS 项目不使用本参考。
+- Java 版本:以项目 `pom.xml`、`build.gradle`、CI 和线上 JDK 为准。Java 17/21 特性只能在确认运行版本支持后使用。
+
+## 源码分析流程
+
+1. **先读结构**:查看目录、包名、模块、构建文件,识别 Controller/Service/Repository/Domain 等分层。
+2. **找入口**:定位 `main` 类、Controller、Message Listener、Scheduled Task、CommandLineRunner 或核心业务入口。
+3. **逐层深入**:沿调用链追踪 Controller -> Service -> Repository/Mapper -> 外部依赖,标注关键分支和异常路径。
+4. **识别依赖**:梳理类间依赖、接口实现、循环依赖、配置注入和第三方组件。
+5. **总结输出**:给出项目概览、核心流程、关键类职责、风险点和验证建议。
+
+### 源码分析输出模板
+
+```markdown
+## 项目概览
+- 技术栈:
+- 分层架构:
+- 构建工具:
+
+## 核心流程
+1. 入口:
+2. 业务逻辑:
+3. 数据访问:
+4. 外部依赖:
+
+## 关键类说明
+| 类名 | 职责 | 关键方法 |
+| --- | --- | --- |
+| ... | ... | ... |
+
+## 质量与风险
+- 正确性:
+- 性能:
+- 安全:
+- 并发:
+- 建议验证:
+```
+
+## Spring Boot 标准结构
+
+```text
+src/main/java/com/example/
+├── config/          # 配置类
+├── controller/      # REST API 层
+├── service/         # 业务逻辑层
+│   └── impl/
+├── repository/      # JPA Repository 或 DAO
+├── mapper/          # MyBatis Mapper
+├── domain/entity/   # 实体类
+├── dto/             # 请求/响应 DTO
+├── exception/       # 自定义异常与统一异常处理
+└── util/            # 无状态工具类
+```
+
+### 常见注解速查
+
+| 场景 | 注解 |
+| - | - |
+| REST Controller | `@RestController`, `@RequestMapping`, `@GetMapping`, `@PostMapping` |
+| 服务层 | `@Service`, `@Transactional` |
+| 仓储层 | `@Repository`, `@Mapper` |
+| 依赖注入 | 构造器注入、`@RequiredArgsConstructor` |
+| 配置 | `@Configuration`, `@Bean`, `@ConfigurationProperties` |
+| 参数校验 | `@Valid`, `@Validated`, `@NotNull`, `@NotBlank`, `@Size` |
+| 缓存 | `@Cacheable`, `@CacheEvict`, `@CachePut` |
+| 异常处理 | `@RestControllerAdvice`, `@ExceptionHandler` |
+
+## 编码规范
+
+- 类名使用 PascalCase,方法/变量使用 camelCase,常量使用 UPPER_SNAKE_CASE。
+- 优先构造器注入,避免字段注入;依赖尽量声明为接口类型。
+- 不吞异常;捕获后必须记录上下文、转换为业务异常或继续抛出。
+- 使用 SLF4J 日志,禁止 `System.out.println()` 进入业务代码。
+- Controller 不写业务细节,Service 不直接拼 HTTP 响应,Repository/Mapper 不承载业务规则。
+- `@Transactional` 放在业务边界;注意自调用、异常类型和只读事务配置。
+- DTO 与 Entity 分离,禁止把 JPA Entity 直接暴露为长期 API 响应契约。
+- 魔法数字、状态码、业务类型优先使用 enum 或常量表达。
+
+## 数据访问
+
+- JPA:关注懒加载、N+1、事务边界、级联删除、脏检查和分页查询。
+- MyBatis:关注 XML/注解 SQL 参数绑定,禁止 `${}` 拼接外部输入,优先 `#{}`。
+- 批量写入:使用批处理或分批提交,明确事务大小与失败回滚策略。
+- 查询性能:为高频 WHERE/JOIN/ORDER BY 字段提供索引依据,必要时协同 `mysql-database`。
+
+## 并发与异步
+
+- 线程池必须显式配置核心线程数、最大线程数、队列长度、拒绝策略和线程名。
+- `CompletableFuture` 需指定 Executor,避免默认 ForkJoinPool 被阻塞任务占满。
+- 共享状态必须说明线程安全策略:不可变对象、局部变量、锁、并发集合或原子类。
+- 分布式场景关注幂等、重试、超时、熔断、降级和链路追踪。
+- `@Async`、调度任务、消息消费必须有异常处理和监控日志。
+
+## 测试
+
+- 单元测试:JUnit 5 + Mockito,隔离纯业务逻辑、边界条件和异常分支。
+- Spring 集成测试:`@SpringBootTest`、`@WebMvcTest`、`@DataJpaTest` 按范围选择,避免所有测试都启动完整上下文。
+- 外部依赖:数据库、Redis、MQ 可使用 Testcontainers 或测试替身。
+- Web 接口:使用 MockMvc 或 WebTestClient 断言状态码、响应体和副作用。
+- 每个修复缺陷至少补一条回归测试。
+
+## JVM 与性能
+
+基础诊断参数示例,使用前需确认线上 JDK、容器内存和运维规范:
+
+```bash
+# 堆内存
+-Xms512m -Xmx2g
+
+# Java 11+ 常用 GC
+-XX:+UseG1GC
+
+# GC 日志
+-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=10m
+```
+
+性能问题优先采集证据:接口耗时、线程 dump、堆 dump、GC 日志、慢 SQL、连接池指标。没有基线不得声称优化有效。
+
+## 验证命令
+
+按项目构建工具选择:
+
+```bash
+mvn test
+mvn verify
+mvn spring-boot:run
+./gradlew test
+./gradlew build
+./gradlew bootRun
+```
+
+## 关联 reference
+
+- `code-generation`:生成 Controller、Service、DTO、Repository、测试代码。
+- `code-review`:按 Java 专项审查清单补充正确性、安全、性能、并发检查。
+- `test-generation`:生成 JUnit、Mockito、Spring Boot Test、集成测试。
+- `performance-benchmark`:JVM、GC、线程池、接口耗时和基准对比。
+- `software-project`:发布、回滚、监控、告警、巡检。

+ 262 - 0
.claude/skills/dev-expert/references/javascript-development.md

@@ -0,0 +1,262 @@
+# JavaScript
+
+> 本文件是 `dev-expert` 的 JS/Node.js 框架专项补充参考,不作为独立子技能计数,也不提供 `@` 显式调用入口。命中 JavaScript、Node.js、ES6+、代码风格、代码检查、PHP 内联 JS 等信号时,按需与 `code-generation`、`api-design`、`bug-diagnosis`、`code-review`、`software-project` 协同加载。
+
+## 适用边界
+
+- 适用:浏览器端现代 JS(ES2017+)、Node.js 服务端开发、JS 代码风格规范、Node.js 代码语法检查与修复、PHP 文件内联 JS 强制校验。
+- 不适用:TypeScript 项目(类型系统由 TS 编译器处理,JSDoc 仅作补充)、非 JS 运行时的前端框架(Flutter/React Native 原生部分)、浏览器 API 兼容性 polyfill 选型、GoF 设计模式教学、JSDoc 文档生成。
+- 运行环境:Node.js >= 15.0.0(推荐 18+),ES 模块优先于 CommonJS。
+
+## 架构规则
+
+1. **const 优先**:默认使用 `const`,仅当变量确实需要重新赋值时才用 `let`,禁止使用 `var`。
+2. **ES 模块优先**:使用 `import`/`export` 代替 `require`/`module.exports`,享受静态分析和 tree-shaking 优势。
+3. **类语法优先**:使用 ES6 `class` 语法代替原型链直接操作,保持代码可读性。
+4. **严格相等**:使用 `===` 和 `!==`,仅在检查 `null`(同时覆盖 `undefined`)时允许 `== null`。
+5. **禁止修改内置原型**:绝不修改 `Array.prototype`、`Object.prototype` 等内置对象的原型。
+6. **显式分号**:所有语句末尾使用显式分号,不依赖自动分号插入(ASI)。
+7. **禁止 `eval` 和 `with`**:永远不使用 `eval()`、`Function` 构造函数和 `with` 语句。
+8. **throw Error 对象**:抛出错误时始终使用 `throw new Error(...)`,不使用 `throw "string"`。
+9. **for-of 优于 for-in**:遍历数组使用 `for-of` 或数组方法(`map`/`filter`/`reduce`),遍历对象键用 `Object.keys()`。
+10. **新抽象必须有真实复用点**:少于 3 个调用点时优先内联,避免过度设计。
+
+---
+
+## 代码质量检查流程
+
+> 确保 JS 代码在本地 Node.js 环境中可成功执行的 4 步标准化工作流:Node.js 版本检查 → 语法检查 → 代码修复 → 可执行性验证。
+
+### 工作流概览
+
+| 步骤 | 检查项 | 执行方式 | 要点 |
+| - | - | - | - |
+| 1 | Node.js 版本 | `node --version` | >= 15.0.0(推荐 18+),低于 15 须升级或在 README 中标注兼容范围 |
+| 2 | 语法正确性 | `node --check <file>` | 静态分析,检测不兼容的 ES6+ 特性、语法错误 |
+| 3 | 代码规范修复 | 人工 / linter 自动修复 | 全角符号转半角、统一引号、补全分号、移除 `var` |
+| 4 | 可执行性验证 | `node -e "<code>"` 或直接运行 | 试运行验证无运行时错误,检查依赖项是否可用 |
+
+### 步骤 1:Node.js 版本检查
+
+执行 `node --version`,确认已安装且版本 >= 15.0.0。若低于 15,需升级 Node.js 或在项目 README 中明确标注兼容范围。推荐使用 18+ LTS 以享受最新语言特性。
+
+### 步骤 2:语法检查
+
+使用 `node --check` 对文件做静态语法分析,无需实际执行代码即可捕获:
+- 语法错误(括号不匹配、非法 token 等)
+- 不兼容的 ES6+ 特性(当 Node 版本过低时)
+- 严格模式违规
+
+示例:`node --check app.js`,退出码 0 表示语法正确。
+
+### 步骤 3:代码规范修复
+
+检查并修复常见编码问题:
+- 全角标点符号(中文逗号、引号)误入代码
+- 文件编码异常(非 UTF-8)
+- 常见语法陷阱(`var` 声明、缺少分号、`==` 应改为 `===`)
+
+可借助 ESLint 的 `--fix` 自动修复大部分问题。全角符号修复需特别注意字符串字面量中的中文内容不被误改。
+
+### 步骤 4:可执行性验证
+
+使用 `node -e` 在隔离环境试运行代码片段,或直接 `node <file>` 执行完整文件,验证:
+- 无运行时错误(`ReferenceError`、`TypeError` 等)
+- 所有 `import` / `require` 的依赖项可正常解析
+- 输出符合预期
+
+若代码依赖 npm 包,需先执行 `npm install` 安装依赖。
+
+### 检查清单
+
+- [ ] Node.js >= 15.0.0(`node --version`)
+- [ ] 语法检查通过(`node --check`)
+- [ ] 无全角符号混入、编码正常
+- [ ] `var` 已替换为 `const`/`let`
+- [ ] 分号完整、`===` 替代 `==`
+- [ ] 可执行,无运行时错误
+- [ ] 外部依赖已安装
+- [ ] **PHP 内联 JS 已按「CMS / PHP 内联 JS」逐条校验**(见下节)
+
+---
+
+## CMS / PHP 内联 JS
+
+> 当 JavaScript 写在 **PHP 文件内**(混编 `<?php ... ?>` 与 HTML、或 `onclick="..."` 事件属性里的内联脚本)时,通用「代码质量检查流程」**不足以**防住一类特有的坑:`php -l` 只校验 PHP 语法,**对混在字符串 / HTML 属性里的 JS 完全无感**,未闭合的引号、括号、空串结尾都会被漏检,最终在浏览器报 `Uncaught SyntaxError`。本节固化此类场景的强制校验项。
+
+### 强制校验 1:HTML 事件属性的引号配对
+
+`onclick="..."` / `onxxx="..."` 这类**双引号 HTML 属性内部,禁止出现裸双引号**——否则属性会提前闭合,后面的 JS 变成裸文本,轻则逻辑失效、重则 XSS。
+
+- ❌ 错误:`onclick="forms["addplfaceform"].submit()"`(`["..."]` 里的 `"` 把属性截断)
+- ✅ 正确(三选一):
+  - 改用方括号 + 单引号:`onclick="forms['addplfaceform'].submit()"`
+  - 或点号访问:`onclick="document.addplfaceform.submit()"`
+  - 或整体抽成函数:`onclick="submitForm('addplfaceform')"`(函数体写在 `<script>`)
+
+> ⚠️ **与「字符串用单引号优先」(风格规范)的冲突说明**:该规范针对**独立 `.js` 文件**。但在 PHP 内联场景,外层 HTML 属性常是双引号,此时**内层 JS 应反过来用单引号 / 点号 / 方括号**,而不是单引号优先——否则极易触发本坑。即:独立 JS 用单引号优先;PHP 内联双引号属性内用单引号/点号访问。
+
+### 强制校验 2:window.open 的 features 串必须以非空串收尾
+
+`window.open(url, name, features)` 的第三个参数(features)**拼接时不得以空字符串 `''` 结尾**,否则浏览器报 `Uncaught SyntaxError: '' string literal contains an unescaped line break`。
+
+- ❌ 错误:`window.open(u,'w','width=900,height=600,left='+(screen.width-900)/2+',top='+(screen.height-600)/2)`(结尾是 `+','` 这种裸拼接 / 或最后一段是 `+'` 空串)
+- ✅ 正确范式:
+  - 坐标用 `Math.floor()` 包裹(避免小数)
+  - 把 `scrollbars=auto,resizable=yes` 放到 features 串**末尾**,使最终拼接段为真实非空串:
+    ```js
+    window.open(
+      u, 'w',
+      'width=900,height=600'
+      + ',left=' + Math.floor((screen.width - 900) / 2)
+      + ',top='  + Math.floor((screen.height - 600) / 2)
+      + ',scrollbars=auto,resizable=yes'
+    );
+    ```
+- 居中坐标公式:`left=Math.floor((screen.width-W)/2)`、`top=Math.floor((screen.height-H)/2)`。
+
+### 强制校验 3:批量改造后必须全仓逐条 Node 校验
+
+改了多个文件的 `window.open` / 内联 JS 后,**不能只靠 `php -l` 或抽查单文件**——须对所有改动点(含存量 `window.open`)逐条做 JS 语法校验:
+
+- 提取每个 `window.open(...)` 的平衡括号片段,用 `node --check` 或 `new Function(code)` 校验;
+- 跳过 `<script src="...">` 外部脚本(不在 PHP 内联范围内);
+- 把 `<?php ... ?>` / `<?= ... ?>` 替换为占位符后再提取,避免 PHP 语法混入 JS 校验。
+
+### 校验流程小结
+
+1. `php -l file.php` —— 仅兜 PHP 语法底,对 JS 无效;
+2. 对每个内联 `window.open` / `onclick` 做「引号配对 + features 收尾非空」双重检查(校验 1、2);
+3. 用 Node 逐条语法校验提取出的 JS 片段(校验 3);
+4. 全部通过才算内联 JS 校验完成。
+
+---
+
+## JS 代码风格规范
+
+> 47 条规则分 8 个分类,按影响优先级排序。为代码生成和代码审查提供风格一致性基准,避免常见的 JS 陷阱。适用浏览器端内联 JS 与独立 `.js` 文件;其中 Node / 构建专属条目(如 `import` 加扩展名、循环依赖)仅适用于独立模块场景。
+
+### 规则分类总览
+
+| 优先级 | 分类 | 影响级别 | 前缀 | 规则数 |
+| - | - | - | - | - |
+| 1 | 模块系统与导入 | CRITICAL | `module-` | 6 |
+| 2 | 语言特性 | CRITICAL | `lang-` | 8 |
+| 3 | 类型安全与 JSDoc | HIGH | `type-` | 6 |
+| 4 | 命名约定 | HIGH | `naming-` | 6 |
+| 5 | 控制流与错误处理 | MEDIUM-HIGH | `control-` | 5 |
+| 6 | 函数与参数 | MEDIUM | `func-` | 5 |
+| 7 | 对象与数组 | MEDIUM | `data-` | 6 |
+| 8 | 格式与风格 | LOW | `format-` | 5 |
+
+### 分类 1:模块系统与导入(CRITICAL)
+
+| 规则 | 说明 |
+| - | - |
+| 避免循环依赖 | 循环导入导致加载失败,重构为单向依赖或延迟导入 |
+| import 加 `.js` 扩展名 | `import { foo } from './bar.js'` 而非 `'./bar'`(仅独立模块场景) |
+| 优先命名导出 | `export const foo` 优于 `export default`,保证重构安全和一致性 |
+| 禁止重复导入 | 同一文件只能 import 一次,合并到单条语句 |
+| 保持原名导入 | 不滥用 `import { foo as bar }`,别名会降低可读性 |
+| 标准文件结构 | 按固定顺序排列:license → imports → 主体代码 |
+
+### 分类 2:语言特性(CRITICAL)
+
+| 规则 | 说明 |
+| - | - |
+| `const` > `let` > 禁用 `var` | const 默认,仅当需要重赋值时用 let |
+| ES6 class 替代 prototype | `class Foo {}` 而非 `Foo.prototype.method = ...` |
+| 显式分号 | 每条语句末尾加分号,不依赖 ASI |
+| 禁止 `eval` / `Function` | 安全风险 + 性能差 |
+| 禁止修改内置原型 | `Array.prototype.myMethod = ...` 永远不允许 |
+| 仅用标准 ECMAScript | 不用非标准扩展(如 `__proto__`、`function.caller`) |
+| 禁止原始值包装对象 | 不用 `new String()` / `new Boolean()` / `new Number()` |
+| 禁止 `with` 语句 | 作用域不可预测,严格模式已禁用 |
+
+### 分类 3:类型安全与 JSDoc(HIGH)
+
+| 规则 | 说明 |
+| - | - |
+| 类型转换用括号 | `/** @type {!Foo} */ (foo)` |
+| 枚举标注字面量 | `/** @enum {string} */` 并列出静态字面值 |
+| 显式可空修饰符 | `{?Type}` 表示可为 null/undefined,不用隐式 `!Type` |
+| 导出函数必须 JSDoc | 所有 `export` 的函数/类都要加 JSDoc 注释 |
+| 模板参数必须指定 | `@template T` 声明后使用 |
+| 复杂类型用 `@typedef` | 对象结构超过 2 层或跨文件复用时定义 `@typedef` |
+
+### 分类 4:命名约定(HIGH)
+
+| 规则 | 说明 |
+| - | - |
+| 常量用 CONSTANT_CASE | `const MAX_SIZE = 100` |
+| 描述性命名优于简短命名 | `getUserById` 而非 `getUsr` |
+| 文件用小写+破折号/下划线 | `user-service.js` 或 `user_service.js` |
+| 方法和变量 lowerCamelCase | `getUserName()`、`let userName` |
+| 禁止 `$` 前缀 | 不用 `$name`、`$$element` |
+| 类名 UpperCamelCase | `class UserController {}` |
+
+### 分类 5:控制流与错误处理(MEDIUM-HIGH)
+
+| 规则 | 说明 |
+| - | - |
+| 空 catch 必须注释 | `catch (e) { // 预期情况:... }` |
+| `for-of` 优于 `for-in` | 遍历数组用 `for-of`,对象键用 `Object.keys()` |
+| 严格相等 `===` | 除 `== null`(同时检查 null/undefined)外一律 `===` |
+| switch 必须有 default | 即使 default 为空,也要显式写出 |
+| throw Error 对象 | `throw new Error('msg')` 而非 `throw 'msg'` |
+
+### 分类 6:函数与参数(MEDIUM)
+
+| 规则 | 说明 |
+| - | - |
+| 嵌套函数优先箭头函数 | 回调用 `() => {}`,保持 `this` 词法绑定 |
+| 箭头参数加括号 | `(x, y) => x + y` 而非 `x, y => x + y` |
+| 默认参数代替条件判断 | `function fn(x = 10)` 而非 `x = x \|\| 10` |
+| rest 参数代替 `arguments` | `(...args) => {}` 而非 `arguments[0]` |
+| spread 代替 `apply` | `fn(...args)` 而非 `fn.apply(null, args)` |
+
+### 分类 7:对象与数组(MEDIUM)
+
+| 规则 | 说明 |
+| - | - |
+| 数组字面量 `[]` 代替 `new Array()` | `const arr = []` |
+| 解构取多个属性 | `const { a, b } = obj` |
+| 禁止混杂引号键 | 全用引号或全不用,不混搭 |
+| 对象字面量 `{}` 代替 `new Object()` | `const obj = {}` |
+| spread 代替 concat/slice | `[...arr1, ...arr2]`、`[...arr].slice(1)` |
+| 多行字面量尾逗号 | `{ a: 1, b: 2, }` |
+
+### 分类 8:格式与风格(LOW)
+
+| 规则 | 说明 |
+| - | - |
+| 控制结构必须用大括号 | `if (x) { return x; }` |
+| 行宽限制 80 字符 | 超长行合理换行 |
+| 每行一条语句 | 不用 `if (x) return x;` 单行 |
+| 字符串用单引号 | `'hello'` 而非 `"hello"` |
+| 两空格缩进 | 不用 Tab |
+
+---
+
+## 常见生产坑
+
+| 场景 | 风险 | 处理方式 |
+| - | - | - |
+| `array.forEach(async fn)` | 异步回调不等待,静默吞错 | 用 `for-of` + `await` 或 `Promise.all(array.map(async fn))` |
+| 忘记清理 Observer 监听器 | 单页应用中内存泄漏 | EventEmitter 的 `on()` 返回取消订阅函数,组件卸载时调用 |
+| 大数组链式操作 | `filter().map().reduce()` 多次遍历 | 合并为 `reduce` 单次遍历,或使用 transducer |
+| `JSON.parse(await res.text())` | 大 JSON 阻塞事件循环 | 流式解析或分块处理 |
+| 循环中创建闭包引用 `var i` | 闭包捕获同一变量引用 | 使用 `let` 或 `for-of` |
+| `Promise.all` 无超时 | 一个 Promise 卡住全部挂起 | 用 `Promise.race([promise, timeout])` 包装 |
+| `new Date(string)` 跨浏览器不一致 | ISO 格式在 Safari 可能报 `Invalid Date` | 统一用 `new Date(year, month-1, day)` 或 `date-fns` |
+| `typeof null === 'object'` | 误判 null 为对象 | 先 `value === null` 再 `typeof value === 'object'` |
+
+---
+
+## 关联 reference
+
+- `code-generation`:生成 JS/Node.js 代码时的语法、风格、模式选择。
+- `api-design`:Express/Koa 等 JS 后端 API 设计规范。
+- `bug-diagnosis`:JS 运行时错误根因分析、内存泄漏排查。
+- `code-review`:基于本规范中的代码风格规范审查 JS 代码。
+- `software-project`:Node.js 项目发布、依赖管理(npm/pnpm/yarn)、CI/CD。

+ 378 - 0
.claude/skills/dev-expert/references/karpathy-coding-guidelines.md

@@ -0,0 +1,378 @@
+# Karpathy编码规范
+
+基于Andrej Karpathy对LLM编码陷阱的观察,提供5条核心编码原则。
+
+## 输入要求
+
+1. **代码片段**(可选):需要评估的代码
+2. **具体问题**(可选):如"这段代码是否过度设计"
+3. **场景描述**(可选):编码场景说明
+
+## 执行流程
+
+### 第一步:明确目标与假设
+
+- 识别用户真实目标和成功标准
+- 标注不确定信息,必要时向用户澄清
+- 列出关键假设,不把猜测隐藏在代码里
+
+### 第二步:应用五大原则
+
+- 用“先证后写”覆盖正确性敏感算法(原则0优先于其余四条)
+- 用“编码前思考”检查需求歧义
+- 用“简洁优先”排除过度设计
+- 用“精准修改”限制变更范围
+- 用“目标驱动执行”定义验证闭环
+
+### 第三步:检查代码或方案
+
+- 对照五大原则逐项检查
+- 识别具体代码位置、改动点或设计风险
+- 区分必须修复和可选优化
+
+### 第四步:输出改进建议
+
+- 给出最小修改方案
+- 提供修改前后对比或明确执行步骤
+- 标注保留现状的理由和风险
+
+### 第五步:验证与沉淀
+
+- 验证建议是否满足原始目标
+- 将可复用的编码规范、陷阱和决策记录到项目记忆
+
+## 五大原则
+
+### 原则0:先证后写(Prove Before Code)
+
+**关键算法先写不变量,再写码。正确性先于简洁。**
+
+编写递归、图遍历、状态机、排序等正确性敏感算法前必须:
+
+- 先书面陈述不变量(invariant)、前置条件、后置条件与终止条件
+- 再据此写出实现,使不变量在每次递归入口/出口成立
+- 用结构化断言验证不变量,而不是依赖"流程跑通"
+
+**反模式**:
+
+- 直接写环检测递归,边写边改,多次迭代都没写对(缺不变量,测试结果错也看不出来)
+- 给关键算法只配"正常值"测试,结构边界(自环/互环/平行边)全空
+
+**正确做法**:
+
+```
+环检测不变量:
+- 前置:图节点集合 G,遍历方向 dir ∈ {down, up, both}
+- 不变量:递归栈 stack 中任意节点 v,其下游环已被正确计数且 v 不重复计入
+- 后置:返回环数与环成员,遍历方向与边有向性解耦
+→ 先有这张表,再写 DFS
+```
+
+```
+符号/边抽取不变量(静态分析类):
+- 前置:① 源码在抽取前已剥离注释/死代码,或抽取层显式排除注释/字符串字面量;② 抽取层按语言隔离——某语言的专属语法正则(如 PHP 的 namespace/use/class/function/new/::)只对该语言文件运行,非目标语言文件(.py/.js/.ts/.java 等)不得套用,否则 .py 的 `class X:`、.js 的 ES6 `class X {}` 会被误抽成目标语言符号污染符号表
+- 不变量:抽取出的每个符号 s ∈ 真实语句 token 集;∀ 边 e=(a,b),a,b 均在真实代码中定义或被引用;且 s 所属语法恰好属于当前文件的目标语言
+- 后置:symbols/edges 中无来自注释/死代码/字符串字面量的伪符号或假边,也无跨语言误抽(非目标语言文件不含目标语言专属符号)
+→ 先有这张表,再写抽取正则(漏剥离任一层、或漏语言隔离,断言都会立刻暴露 phantom/跨语言误抽,而非上线后静默成假边)
+```
+
+原则0 优先于其余四条:简洁、精准、目标驱动都不能以牺牲正确性为代价。
+
+#### 不变量推导三步法(原则0 的执行方法)
+
+原则0 要求"先写不变量再写码",但没讲不变量怎么来。任何图遍历 / 递归 / 状态机 / 排序 / 依赖解析算法,**先按下面三步把不变量显式推出来,再动码**——边界用例表(见 `test-generation.md`)是"授人以鱼",这三步是"授人以渔",P8 面对任意新算法能自己推出不变量,靠的是这个。
+
+**第一步:找循环保持量(Loop Invariant)**
+
+> 定义:每次循环 / 递归迭代前后都恒真的命题。
+
+连问三句:
+
+1. 算法"已经正确处理完的部分"是什么?把它写成一句命题;
+2. 若算法在任意时刻被强制中断,什么仍然成立?
+3. 这个命题在 base case(初始态)为真、且在 induction step(每次迭代后)仍为真吗?
+
+**第二步:找终止度量(Termination Variant)**
+
+> 定义:每次迭代严格递减、且有下界的量。
+
+问一句:"每次递归,什么量在严格变小?它的下界是什么?"
+
+- 环检测示例:未访问节点数严格递减,下界 0 → 必终止。
+- 若找不到严格递减且有下界的量,递归可能不终止,必须在动码前先修正设计。
+
+**第三步:找状态守恒(State Conservation)**
+
+> 定义:算法维护的多个状态量之间必须满足的守恒关系。
+
+问一句:"哪些量必须守恒?违反守恒就等于出 bug?"
+
+- 环检测示例(三条守恒):① 环计数不重不漏;② 遍历方向与边有向性一致;③ 平行边不翻倍。
+
+**推导自检清单(交出草稿不变量后,先自证再写码)**:
+
+1. **可判定性**:能写成可断言的布尔命题,而不是"大概对";
+2. **初始成立**:base case 下命题为真;
+3. **归纳保持**:每次迭代后仍为真;
+4. **充分性**:不变量 ∧ 终止条件 ⇒ 结果正确。
+
+**落地硬规则**:遇到关键算法,先交出「不变量命题 + 终止度量 + 守恒关系」三件套;**交不出、或过不了自检清单,禁止开始写码,也禁止声称"修复完成"**。
+
+### 原则1:编码前思考(Think Before Coding)
+
+**不要假设。不要隐藏困惑。暴露权衡。**
+
+编码前必须:
+
+- 明确陈述假设——如果不确定,提问而不是猜测
+- 存在多种理解时,列出选项——不要默默选择
+- 存在更简单方案时,说出来——该推回时推回
+- 遇到不清楚的地方,停下来——说出困惑所在,提问
+
+**反模式**:
+
+- 用户说"导出用户数据",直接写导出所有用户的代码(假设了范围、格式、字段)
+- 用户说"让搜索更快",直接加缓存和索引(假设了"更快"的含义)
+
+**正确做法**:
+
+```
+导出用户数据前,我需要澄清:
+1. 范围:导出全部用户还是筛选后的子集?
+2. 格式:JSON/CSV/直接API返回?
+3. 字段:哪些字段?(有些可能涉及隐私)
+```
+
+### 原则2:简洁优先(Simplicity First)
+
+**最少代码解决问题。不要 speculative。**
+
+- 不要超出需求的功能
+- 不要为单次使用代码做抽象
+- 不要未被要求的"灵活性"或"可配置性"
+- 不要为不可能场景做错误处理
+- 写了200行但50行能搞定,重写
+
+**自检问题**:"资深工程师会说这过度复杂吗?"如果是,简化。
+
+**反模式**:
+
+- 用户说"计算折扣",生成Strategy模式+抽象类+配置对象(30行设置做简单计算)
+- 用户说"保存用户偏好",加缓存、验证、合并、通知(没人要求的功能)
+
+**正确做法**:
+
+```python
+# 简单直接
+def calculate_discount(amount: float, percent: float) -> float:
+    return amount * (percent / 100)
+```
+
+### 原则3:精准修改(Surgical Changes)
+
+**只碰必须碰的。只清理自己制造的。**
+
+编辑现有代码时:
+
+- 不要"改进"相邻代码、注释或格式
+- 不要重构没坏的东西
+- 匹配现有风格,即使你自己会不同做法
+- 发现无关死代码,提一下——但不要删
+
+你的修改制造孤儿时:
+
+- 删除**你的修改**导致的未使用import/变量/函数
+- 不要删除预先存在的死代码,除非被要求
+
+**测试标准**:每一行修改都能追溯到用户的请求。
+
+**反模式**:
+
+- 修复空邮箱Bug时,顺便"改进"邮箱验证、加用户名验证、改注释、加docstring
+- 添加上传日志时,改引号风格、加类型提示、重排空白
+
+**正确做法**:
+
+```diff
+-     if not user_data.get('email'):
++     email = user_data.get('email', '')
++     if not email or not email.strip():
+          raise ValueError("Email required")
+```
+
+### 原则4:目标驱动执行(Goal-Driven Execution)
+
+**定义成功标准。循环直到验证。**
+
+把指令式任务转化为可验证目标:
+
+- "加验证" → "写无效输入的测试,然后让它们通过"
+- "修Bug" → "写复现Bug的测试,然后让它通过"
+- "重构X" → "确保重构前后测试都通过"
+
+多步骤任务时,陈述简要计划:
+
+```
+1. [步骤] → 验证:[检查]
+2. [步骤] → 验证:[检查]
+3. [步骤] → 验证:[检查]
+```
+
+强成功标准让你独立循环。弱标准("让它工作")需要持续澄清。
+
+## 失败回退机制
+
+| 失败场景 | 回退目标 | 最大重试 |
+| - | - | - |
+| 目标模糊("让它工作"类) | 拆分为可验证子项,附验证方法 | 2 |
+| 歧义无法澄清 | 列2-3种最可能理解,标注"假设" | 2 |
+| 需求边界模糊 | 保守保留核心功能,标注"待确认" | 1 |
+| 测试环境/依赖不可用 | 最小化方式验证核心逻辑(直接调用+断言) | 2 |
+| 验证失败(外部依赖问题) | 标注"外部依赖异常",记录复现条件 | 1 |
+| 连续3轮未通过验证 | 暂停循环,产出当前最佳版本+差异报告 | 0 |
+
+不可恢复时输出阻塞清单和升级路径,由用户决策。
+
+## 输出格式
+
+```
+## Karpathy编码规范评估
+
+**评估代码**:[文件名/片段]
+
+### 原则0:先证后写
+- [ ] 是否涉及正确性敏感算法(递归/图遍历/状态机/排序)?
+- [ ] 关键算法是否先写不变量、前置/后置/终止条件?
+- [ ] 是否用结构化断言验证不变量,而非"流程跑通"?
+
+**评估**:[通过/需改进/不适用]
+**问题**:[具体问题]
+**建议**:[改进建议]
+
+### 原则1:编码前思考
+- [ ] 假设是否明确陈述?
+- [ ] 是否存在未澄清的歧义?
+- [ ] 是否遗漏了更简单的方案?
+
+**评估**:[通过/需改进]
+**问题**:[具体问题]
+**建议**:[改进建议]
+
+### 原则2:简洁优先
+- [ ] 是否包含超出需求的功能?
+- [ ] 是否存在单次使用的抽象?
+- [ ] 是否可以更短更简单?
+
+**评估**:[通过/需改进]
+**问题**:[具体问题]
+**建议**:[简化方案]
+
+### 原则3:精准修改
+- [ ] 是否只修改了必要的代码?
+- [ ] 是否保持了原有风格?
+- [ ] 是否清理了自己的孤儿代码?
+
+**评估**:[通过/需改进]
+**问题**:[具体问题]
+**建议**:[改进建议]
+
+### 原则4:目标驱动执行
+- [ ] 是否有明确的验证标准?
+- [ ] 是否可以独立验证每一步?
+
+**评估**:[通过/需改进]
+**问题**:[具体问题]
+**建议**:[改进建议]
+
+## 总体评估
+
+**符合度**:[X]/100
+**主要问题**:[Top 1-2问题]
+**优先修复**:[具体建议]
+```
+
+## 反 AI 糟粕代码门禁
+
+生成或审查代码时,以下模式必须作为质量门禁逐项排查:
+
+| 门禁项 | 典型表现 | 处理规则 |
+| - | - | - |
+| 过度抽象 | 为一次性逻辑创建策略模式、工厂、管理器、配置层 | 删除抽象,回到直接、可读、可测的实现 |
+| 废注释 | 注释重复代码含义、为显而易见的赋值写解释 | 删除废注释,仅保留解释业务约束、边界条件、危险操作的注释 |
+| 模板化错误处理 | 每个函数机械包 `try/catch`,catch 内吞异常或只返回空值 | 让异常在合适层级处理;catch 必须记录、转换或给用户可见反馈 |
+| 防御式臆想 | 为未出现的输入、未要求的插件点、未知未来需求堆分支 | 删除未被需求或测试证明的分支 |
+| 全文件重写 | 为小修复重排格式、重命名无关变量、搬动无关函数 | 回退到局部修改,只触碰完成目标所需代码 |
+| 不可验证产物 | 只给代码,不给 lint/test/build/curl/手动验收路径 | 补齐最小验证命令或说明无法验证的具体阻塞 |
+
+交付前必须回答:
+
+1. 新增代码是否直接服务于用户目标?
+2. 是否存在可删除而不影响功能的抽象、注释或配置?
+3. 是否触碰了与任务无关的文件、函数或格式?
+4. 是否有明确验证证据支撑"已完成"?
+
+## 静态分析硬门禁
+
+交付关键算法或修复前,以下静态检查作为硬门禁,必须通过或显式说明豁免理由:
+
+| 门禁项 | 工具/动作 | 处理规则 |
+| - | - | - |
+| Lint | 语言对应 linter(PHP `php -l`、ESLint、ruff、golangci-lint 等) | 语法/静态错误 0 容忍,必须本地跑通后再交付 |
+| 类型检查 | `mypy` / `tsc --noEmit` / `phpstan` 等 | 类型错误必须修复或标注 `# type: ignore` 并说明原因 |
+| 死代码检测 | `vulture` / `pyflakes` / IDE 未使用检测 | 删除未使用 import/变量/函数,或说明保留理由 |
+| 测试门禁 | 关键算法必须跑通算法边界用例(见 `test-generation.md`) | 结构边界用例不过,不得声称"修复完成" |
+
+未跑静态分析门禁即交付,视为未验证产物,按「反 AI 糟粕代码门禁」的"不可验证产物"处置。
+
+## 依赖管理锁定
+
+新增、升级或锁定依赖时,必须遵守以下规则,避免构建漂移和环境差异。
+
+### 强制规则
+
+| 规则 | 说明 | 反例 |
+| - | - | - |
+| 版本锁定 | 所有依赖必须使用锁文件精确锁定版本 | `composer.json` 写 `"laravel/framework": "^10.0"` 但未提交 `composer.lock` |
+| 禁止浮动版本 | 业务代码禁止使用 `latest` / `*` / `dev-main` / `@stable` 等浮动约束 | `"vue": "latest"` |
+| 锁文件入库 | `composer.lock` / `package-lock.json` / `pnpm-lock.yaml` / `yarn.lock` 必须提交到代码库 | `.gitignore` 排除锁文件 |
+| 子依赖锁定 | 不依赖平台自动解析子依赖版本,以锁文件全量快照为准 | 仅锁顶层依赖,子依赖由 registry 解析 |
+| 新增依赖必审 | 新增依赖必须说明用途、替代方案和体积影响,禁止为单次任务引入重框架 | 为计算折扣引入整个 `symfony/expression-language` |
+| 升级必分批 | 升级依赖必须分 minor → major 升级,每级跑测试;禁止跨大版本一步升级 | 直接从 PHP 7.4 跳到 8.3 不验证 |
+| 漏洞依赖处置 | 发现 CVE 必须在 7 天内升级或替换,未升级前标注为已知风险 | 已知漏洞依赖长期留存 |
+
+### 检查清单
+
+交付前逐项确认:
+
+- [ ] 锁文件存在且与 `composer.json` / `package.json` 一致
+- [ ] 业务代码无 `latest` / `*` / `dev-*` 浮动约束
+- [ ] 新增依赖已说明用途和替代方案
+- [ ] 升级路径经过分阶段测试
+- [ ] 已知漏洞依赖已标注或已处置
+
+### 失败回退
+
+| 失败场景 | 回退动作 |
+| - | - |
+| 锁文件与配置不一致 | 用 `composer install` / `npm ci` 重新生成锁文件后提交 |
+| 升级后测试失败 | 回退到上一稳定锁文件,分文件分模块排查 |
+| 新增依赖体积过大 | 评估轻量替代方案,必要时拒绝引入 |
+
+## 质量标准
+
+- 评估必须基于代码实际内容,不能泛泛而谈
+- 每个原则的评估必须有具体的代码位置引用
+- 改进建议必须提供具体的修改前后对比
+- 不得为符合规范的代码强行找问题
+- 评估应平衡:不是追求绝对完美,而是避免常见陷阱
+- 必须逐项检查「反 AI 糟粕代码门禁」,发现命中项时优先压缩或删除
+- 不能用新增抽象、泛化配置或模板化错误处理替代真正的业务理解
+- 关键算法交付前必须通过静态分析门禁(lint/类型/死代码/边界测试),未通过不得声称完成
+- 正确性敏感算法必须先写不变量(原则0)再实现,不得边写边猜
+
+## 关联 reference
+
+- **code-review**(代码审查)— 用 `code-review` 进行全面的技术审查(Bug/安全/性能)
+- **code-generation**(代码生成)— 用 `code-generation` 生成功能代码,再用本Skill评估是否符合规范
+- **refactoring**(重构建议)— 用 `refactoring` 获取结构优化方案,再用本Skill检查是否过度设计
+- **project-memory-management**(项目记忆管理)— 记录可复用的编码规范、陷阱和决策清单

+ 92 - 0
.claude/skills/dev-expert/references/laravel-development.md

@@ -0,0 +1,92 @@
+# Laravel 专项开发
+
+> 本文件是 `dev-expert` 的 Laravel/PHP 框架专项补充参考,不作为独立子技能计数,也不提供 `@` 显式调用入口。命中 Laravel、Eloquent、Blade、artisan、Migration、Form Request、Queue、PHPUnit、PHPStan 等信号时,按需与 `code-generation`、`cms-development`、`mysql-database`、`api-design`、`software-project` 协同加载。
+
+## 适用边界
+
+- 适用:Laravel 应用开发、Eloquent 查询、Blade、API Resource、Form Request、Queue/Job、Migration、PHPUnit/Pest、PHPStan、部署缓存与性能配置。
+- 不适用:PHP 内核开发、非 Laravel CMS 内核改造、未确认框架类型时的 Laravel 专用代码生成。
+- PHP 版本:以项目 `composer.json`、CI 和线上环境为准。Laravel 新项目可优先 PHP 8.2+;使用 PHP 8.4 特性前必须确认运行环境支持。
+
+## 架构规则
+
+1. Controller 保持薄层:只做认证授权、请求校验、调用服务/Action、返回 Response。
+2. 业务编排优先放 Service 或单用途 invokable Action;模型只承载关系、scope、accessor、mutator 和领域内聚行为。
+3. HTTP 边界使用 Form Request;复杂输入通过 `toDto()` 转成类型明确的参数对象。
+4. API 输出使用 Resource 或 Resource Collection,禁止 Controller 直接返回 raw model / `toArray()` 作为长期契约。
+5. 副作用使用 Event/Listener 或 Job 承接,避免在 Controller 中直接发通知、写缓存、调第三方。
+6. 新抽象必须有真实复用点;少于 3 个调用点时优先内联,避免过度分层。
+
+## Laravel API 设计
+
+- 契约优先:先定义 Form Request 输入契约和 API Resource 输出契约,再写 Controller。
+- 错误响应统一 envelope,例如 `{ "success": false, "error": { "code": "...", "message": "..." } }`。
+- 新增字段优先于修改/删除字段;删除字段必须先标记 deprecated 并给迁移窗口。
+- 第三方 API 响应一律视为不可信数据,进入业务逻辑前用 DTO 或专门校验类约束结构。
+- 多租户/父子资源使用 scoped route model binding,避免跨租户越权。
+
+## Eloquent 与查询规则
+
+- 开发环境启用 `Model::preventLazyLoading(!app()->isProduction())` 捕获 N+1。
+- 只选择必要列:`select(['id', 'title', 'user_id'])`,关联加载也指定列。
+- 计数用 `withCount()` / `withExists()`,禁止加载完整关系后再 count。
+- 批量更新优先数据库级操作;但涉及 observer、audit、事件、文件清理时必须逐模型 `save()` 或显式说明绕过原因。
+- 大数据处理使用 `chunkById()` / lazy collection,并明确并发写入风险。
+- 批量插入/更新优先 `upsert()`,并说明唯一键和更新列。
+- 模型禁止 `$guarded = []`,必须使用显式 `$fillable` 或受控 DTO。
+
+## 常见生产坑
+
+| 场景 | 风险 | 处理方式 |
+| - | - | - |
+| `Model::where()->update()` | 跳过 Eloquent observer、audit、model events | 需要事件时用 `lockForUpdate() + save()`;确实批量绕过时写明原因 |
+| `attach/detach/sync/updateExistingPivot` | 直接写 pivot 表,不触发 pivot model events | 需要审计时把 pivot 建成真实模型并通过模型写入 |
+| Observer 删除文件 | 父级路径清理可能误删兄弟记录文件 | 删除动作限定到当前记录路径,复杂清理交给 Action |
+| `chunkById + json_decode + update` | JSON 字段并发写入被旧快照覆盖 | 浅层修改用 DB 原子表达式;复杂修改需锁或维护窗口 |
+| `DB::afterCommit()` | 只避免回滚时执行,不负责提交后失败重试 | 外部副作用默认改成 queued job + retry + failed 处理 |
+| JsonResource 直接返回 Carbon | 可能绕过 model cast 的日期格式 | API 日期格式在 Resource 内显式 format 并测试 |
+| 嵌套数组只写 `items.*.field` | 标量元素可能通过局部规则导致运行时报错 | 同时加 `items.* => array` |
+
+## Migration 与数据变更
+
+- 已在共享环境执行过的 migration 禁止回改;新增 migration 处理后续变更。
+- DDL 和数据回填分离;大表变更优先 expand-contract:新增列 -> 回填 -> 切读写 -> 删除旧列。
+- 外键用 `foreignId()->constrained()`,频繁过滤列要加索引并说明查询路径。
+- 需要真正逐行提交/释放锁的 PostgreSQL migration,应设置 `$withinTransaction = false`;MySQL DDL 多数会隐式提交,仍需写回滚方案。
+- 数据回填必须可重入,避免 `migrate:fresh` 或重跑时重复插入/误覆盖。
+
+## Queue 与异步任务
+
+- Job 必须实现失败路径:`failed(Throwable $exception)`、重试次数、退避策略、幂等键。
+- 顺序依赖用 `Bus::chain()`,批处理用 `Bus::batch()`。
+- 对外部 API 调用加限流和超时;重复请求用唯一键或 `ShouldBeUnique`。
+- 状态变更 + 外部副作用优先使用事务后派发 job,并补偿失败状态。
+- **与 Init-Step-Poll 桥接**:需向前端暴露进度 / 允许取消的长任务(批量导入导出 / 生成静态页 / 采集同步),用 `Init → Step → Poll` 包装 Queue Job——Init 派发 Job 并建任务记录、Step(或 Job 内部批处理)更新进度、Poll 查任务状态;纯后端异步可直接用裸 Queue,无需 Init-Step-Poll 包装。
+
+## 生产就绪
+
+- 启动期校验关键配置:API key、DSN、queue、cache、storage,缺失时 fail fast。
+- 提供 `/health` 和 `/ready`:前者检查进程可响应,后者检查 DB/Redis/关键依赖。
+- 部署前执行 `config:cache`、`route:cache`、`view:cache`,并确认动态配置不依赖运行时 `.env` 变更。
+- OPcache、preload、JIT 只在确认 PHP 版本、框架兼容和回滚方式后开启。
+
+## 验证命令
+
+按项目实际工具选择,至少保留一条可复现证据:
+
+```bash
+composer validate
+composer test
+./vendor/bin/phpunit
+./vendor/bin/phpstan analyse --level=8
+php artisan test
+php artisan route:list
+```
+
+## 关联 reference
+
+- `code-generation`:实现 Controller、Service、Action、Job、Resource。
+- `api-design`:接口契约、错误 envelope、版本策略、长任务 Init-Step-Poll。
+- `mysql-database`:索引、事务、慢查询、迁移回滚。
+- `test-generation` + `laravel-testing`:Feature Test、Factory、Fake/Mock。
+- `software-project`:发布、回滚、监控、告警、巡检。

+ 114 - 0
.claude/skills/dev-expert/references/laravel-testing.md

@@ -0,0 +1,114 @@
+# Laravel 测试专项参考
+
+> 本文件是 `test-generation` 的 Laravel/PHPUnit 补充参考,不作为独立子技能计数。命中 Laravel、PHPUnit、Pest、Feature Test、Factory、Queue fake、HTTP fake、Sanctum 等信号时按需加载。
+
+## 测试分层
+
+- Feature Test:覆盖 HTTP、路由、中间件、授权、校验、数据库副作用,是 Laravel 业务功能的默认优先级。
+- Unit Test:覆盖纯服务、Action、值对象、规则类,不依赖 HTTP,尽量少碰数据库。
+- Integration Test:覆盖队列、外部服务适配器、缓存、文件系统等跨边界行为。
+- Regression Test:每个已修复缺陷至少补一条失败先行或可复现用例。
+
+## 基础规则
+
+1. 一个测试只验证一个主要行为,命名使用 `test_` 前缀或清晰的 `it ...` 描述。
+2. 同时断言响应和副作用:HTTP status、JSON 结构、数据库状态、队列/事件/通知是否派发。
+3. 所有测试数据优先使用 Factory,禁止在业务测试里散落 raw `DB::table()->insert()`。
+4. Fake 必须在 action 前设置:先 `Queue::fake()`,再执行请求,最后断言。
+5. 外部 HTTP、邮件、通知、队列、事件、Storage 默认 fake,除非当前测试明确验证真实集成。
+6. 涉及权限时同时测允许路径和拒绝路径。
+
+## 数据库策略
+
+- `RefreshDatabase`:默认选择,保证 migration 与测试数据库一致。
+- `DatabaseTransactions`:更快,但不能验证 migration;仅在项目明确适配时使用。
+- `DatabaseMigrations`:每个测试运行/回滚 migration,成本高,只在需要验证迁移行为时使用。
+- 数据库断言使用 `assertDatabaseHas()`、`assertDatabaseMissing()`;金额 DECIMAL 按字符串断言,避免 float 精度问题。
+
+## Feature Test 模板
+
+```php
+public function test_user_can_update_own_profile(): void
+{
+    Queue::fake();
+
+    $user = User::factory()->create();
+
+    $response = $this
+        ->actingAs($user)
+        ->patchJson('/api/profile', [
+            'name' => 'Ada Lovelace',
+        ]);
+
+    $response
+        ->assertOk()
+        ->assertJsonPath('success', true)
+        ->assertJsonPath('data.name', 'Ada Lovelace');
+
+    $this->assertDatabaseHas('users', [
+        'id' => $user->id,
+        'name' => 'Ada Lovelace',
+    ]);
+
+    Queue::assertNothingPushed();
+}
+```
+
+## 校验与授权测试
+
+- 表单校验:断言 422、字段错误、不会产生数据库副作用。
+- 授权拒绝:断言 403 或项目统一错误 envelope。
+- 认证缺失:断言 401,并确认不会泄露内部错误。
+- API Resource:断言字段存在、敏感字段不存在、关系字段只在 `whenLoaded()` 时出现。
+
+## Factory 模式
+
+- 每个核心模型都应有 Factory。
+- 状态使用 `state()`:如 `active()`、`suspended()`、`verified()`。
+- 关系通过 Factory 组合创建,不在测试主体里手写大量依赖数据。
+- 需要副作用的测试可使用 `afterCreating()`,但要避免隐藏过多业务前置条件。
+- 批量场景使用 sequence 覆盖不同状态和边界值。
+
+## Fake 与 Mock
+
+| 工具 | 用途 | 断言 |
+| - | - | - |
+| `Queue::fake()` | 队列任务 | `assertPushed()`、`assertNothingPushed()` |
+| `Event::fake()` | 领域事件 | `assertDispatched()` |
+| `Notification::fake()` | 通知 | `assertSentTo()` |
+| `Mail::fake()` | 邮件 | `assertSent()`、`assertQueued()` |
+| `Storage::fake()` | 上传/文件写入 | `assertExists()`、`assertMissing()` |
+| `Http::fake()` | 外部 API | `assertSent()`、模拟超时/错误 |
+| `Bus::fake()` | chain/batch | `assertChained()`、`assertBatched()` |
+
+服务类 mock 只用于隔离外部边界或昂贵依赖;核心业务规则优先真实执行,避免测试只验证 mock 调用。
+
+## N+1 与性能回归
+
+- 对列表接口记录查询数量基线,新增关系字段时必须复测。
+- 使用 `with()`、`withCount()`、`withExists()` 后断言响应字段和查询数量。
+- 性能断言不替代功能断言;如需量化性能,协同 `performance-benchmark`。
+
+## 长任务与队列测试
+
+- Init-Step-Poll 接口需覆盖:初始化成功、重复初始化幂等、单步成功、单步失败、轮询完成、轮询失败。
+- Job 需覆盖:成功路径、异常重试、幂等重复执行、`failed()` 补偿。
+- 批处理需覆盖:`then`、`catch`、`finally` 的状态变更。
+
+## 运行命令
+
+```bash
+php artisan test
+./vendor/bin/phpunit
+./vendor/bin/phpunit --filter test_user_can_update_own_profile
+./vendor/bin/phpstan analyse --level=8
+```
+
+## 交付证据
+
+交付 Laravel 测试相关任务时,至少提供:
+
+- 新增/修改的测试文件路径。
+- 运行命令。
+- 通过/失败结果。
+- 如失败,给出阻塞原因和下一步所需输入。

+ 355 - 0
.claude/skills/dev-expert/references/mysql-database.md

@@ -0,0 +1,355 @@
+# MySQL数据库 -- 数据建模、SQL安全与性能优化
+
+面向 PHP/CMS/网站项目中的 MySQL 数据库设计、SQL 编写、索引优化、慢查询诊断、迁移回滚和数据安全场景。目标是在写代码前先明确表结构、访问路径、事务边界和验证方法,避免后期靠补丁修数据库问题。
+
+## 输入要求
+
+### 必填信息
+
+1. **业务场景**:要存储/查询/统计/更新什么数据
+2. **操作类型**:建表 / 改表 / 查询 / 写入 / 统计 / 慢查询优化 / 数据迁移
+3. **数据规模**:当前行数、预计增长、单次处理量、读写比例
+4. **数据库版本**:MySQL 5.7 / 8.0 / MariaDB,未知则标注假设
+5. **调用环境**:PHP 原生 / CMS 数据访问层 / Laravel / ThinkPHP / WordPress / EmpireCMS
+
+### 可选信息
+
+- 现有表结构:`SHOW CREATE TABLE`
+- 现有 SQL 和参数
+- `EXPLAIN` / `EXPLAIN ANALYZE` 结果
+- 慢查询日志片段
+- 线上约束:是否允许停机、是否大表、是否可加索引
+- 事务/一致性要求
+
+## 执行流程
+
+### 第一步:识别数据库任务类型
+
+先判断当前任务属于哪一类:
+
+| 类型 | 触发关键词 | 优先动作 |
+| - | - | - |
+| 表结构设计 | 建表、字段、模型、Schema | 输出 DDL + 字段说明 + 索引 |
+| SQL 编写 | 查询、筛选、分页、统计 | 输出参数化 SQL + 绑定参数 |
+| 慢查询优化 | 慢、卡、超时、EXPLAIN | 先读执行计划,再改 SQL/索引 |
+| 数据迁移 | ALTER、迁移、导入、修数据 | 输出备份、分批、回滚方案 |
+| CMS 数据库 | 表前缀、模型表、副表、options | 调用 CMS二次开发规范 |
+
+### 第二步:确认版本、字符集和会话设置
+
+默认推荐:
+
+```sql
+SET NAMES utf8mb4;
+SET time_zone = '+08:00';
+SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
+```
+
+建库建表必须优先使用 `utf8mb4`,排序规则优先 `utf8mb4_unicode_ci` 或项目现有规则。
+
+### 第三步:表结构设计
+
+#### 基础规范
+
+| 项 | 规则 |
+| - | - |
+| 引擎 | 默认 InnoDB;只有明确 CMS 历史约束时保留 MyISAM |
+| 主键 | `BIGINT UNSIGNED AUTO_INCREMENT` |
+| 字符集 | 表和字段统一 `utf8mb4` |
+| 时间字段 | `created_at` / `updated_at` 使用 `DATETIME(3)` |
+| 软删除 | 需要恢复/审计时加 `deleted_at` 并建索引 |
+| 金额 | 使用 `DECIMAL`,PHP 侧按 string 处理 |
+| JSON | MySQL 5.7+ 可用 JSON;旧版本用 TEXT + JSON 校验 |
+
+#### 字段类型选择
+
+| 场景 | 类型 |
+| - | - |
+| 主键/外键 | `BIGINT UNSIGNED` |
+| 状态/枚举 | `TINYINT UNSIGNED` |
+| 金额 | `DECIMAL(12,2)` 或按业务精度调整 |
+| 短文本 | `VARCHAR(N)` |
+| 长文本 | `TEXT` / `MEDIUMTEXT` |
+| 布尔 | `TINYINT(1)` |
+| 固定散列 | `CHAR(32)` / `CHAR(64)` |
+
+### 第四步:索引设计
+
+必须先根据查询路径设计索引,禁止“看起来可能会用”就加索引。
+
+#### 索引策略决策树
+
+1. **先列查询路径**:WHERE、JOIN、ORDER BY、GROUP BY、分页、唯一约束分别列出。
+2. **再看数据分布**:字段基数、重复率、NULL 比例、冷热数据范围。
+3. **再定索引类型**:主键、唯一索引、普通索引、联合索引、前缀索引、全文索引。
+4. **最后用 EXPLAIN 验证**:确认 `key`、`type`、`rows`、`Extra` 是否符合预期。
+
+#### 基础规则
+
+| 规则 | 要求 |
+| - | - |
+| 命名 | 普通索引 `idx_表名_字段`,唯一索引 `uk_表名_字段` |
+| 联合索引 | 遵循最左前缀,等值字段在前,范围/排序字段靠后 |
+| 数量 | 普通业务表不超过 5 个,大表不超过 8 个 |
+| VARCHAR 前缀 | utf8mb4 下索引长度注意 191 字符限制 |
+| 禁止 | 低区分度字段单独建索引、频繁更新字段滥建索引 |
+
+#### 联合索引字段顺序
+
+推荐顺序:
+
+```text
+等值过滤字段 → 高区分度字段 → 范围字段 → 排序字段 → 覆盖返回字段
+```
+
+示例:
+
+```sql
+WHERE tenant_id = ?
+  AND status = ?
+  AND created_at >= ?
+ORDER BY created_at DESC
+LIMIT 20
+```
+
+推荐索引:
+
+```sql
+KEY idx_orders_tenant_status_created (tenant_id, status, created_at)
+```
+
+注意:
+
+- 范围字段后面的字段通常不能继续用于高效过滤。
+- `ORDER BY` 方向要和索引顺序兼容;MySQL 8.0 支持降序索引。
+- 多租户表优先把 `tenant_id` 放在联合索引左侧。
+
+#### 查询场景策略
+
+| 场景 | 索引策略 | 禁忌 |
+| - | - | - |
+| 精确查询 | 唯一键或高区分度普通索引 | 给低区分度状态字段单独建索引 |
+| 列表分页 | 过滤字段 + 排序字段联合索引 | 大 offset 深分页 |
+| 深分页 | 使用游标/last_id 翻页 | `LIMIT 100000,20` |
+| JOIN | 关联字段两侧类型一致,子表关联字段建索引 | 字段类型/字符集不一致 |
+| ORDER BY | 让排序字段进入联合索引 | 依赖 filesort 处理大结果集 |
+| GROUP BY | 分组字段建索引或先缩小数据集 | 全表分组统计 |
+| LIKE 前缀 | `LIKE 'abc%'` 可用索引 | `LIKE '%abc%'` 期待普通索引命中 |
+| 全文搜索 | FULLTEXT 或搜索引擎 | 在大 TEXT 上滥用 LIKE |
+| 唯一约束 | 用唯一索引保障业务唯一性 | 只在代码层判断唯一 |
+
+#### 覆盖索引策略
+
+高频列表页可用覆盖索引减少回表:
+
+```sql
+KEY idx_article_cat_status_time_id (cat_id, status, publish_time, id)
+```
+
+适用:
+
+- 列表页只返回少量字段。
+- 查询频率高、返回行数少。
+- 回表成本明显。
+
+限制:
+
+- 不要为了覆盖索引把大字段、TEXT、长 VARCHAR 塞进索引。
+- 覆盖索引增加写入成本,必须说明收益。
+
+#### 冗余索引治理
+
+必须检查已有索引,避免重复:
+
+| 已有索引 | 新索引 | 判断 |
+| - | - | - |
+| `(a,b)` | `(a)` | 通常冗余 |
+| `(a,b)` | `(a,b,c)` | 可能可合并,需看查询 |
+| `(a)` | `(b,a)` | 不等价 |
+| `uk_email(email)` | `idx_email(email)` | 冗余 |
+
+删除索引前必须:
+
+1. 列出依赖 SQL。
+2. 确认没有唯一约束语义。
+3. 在测试环境用 EXPLAIN 对比。
+4. 说明回滚 SQL。
+
+#### 写入成本评估
+
+新增索引不是免费优化,必须评估:
+
+- INSERT/UPDATE/DELETE 是否变慢。
+- 索引是否会放大磁盘占用。
+- 频繁更新字段是否导致页分裂。
+- 大表加索引是否需要在线 DDL。
+
+#### CMS 常见索引策略
+
+| CMS 场景 | 推荐策略 |
+| - | - |
+| 栏目内容列表 | `classid/status/newstime` 或项目等效字段联合索引 |
+| 后台搜索 | 关键词字段不做 `%kw%` 普通索引幻想,改全文索引或搜索服务 |
+| 订单/表单列表 | `site_id/status/created_at` 联合索引 |
+| 软删除 | `deleted_at` 进入常用查询联合索引 |
+| 多站点/多租户 | `site_id` / `tenant_id` 放联合索引左侧 |
+
+#### EXPLAIN 验收标准
+
+| 字段 | 目标 |
+| - | - |
+| `type` | 至少达到 `range`,高频点查应为 `ref` / `const` |
+| `key` | 命中预期索引 |
+| `rows` | 扫描行数与业务结果数量同量级 |
+| `Extra` | 高频查询避免大规模 `Using temporary` / `Using filesort` |
+
+如果不能满足,必须说明原因和替代方案。
+
+### 第五步:SQL安全与参数绑定
+
+所有外部输入必须参数绑定,内部接口也不例外。
+
+| 场景 | 禁止 | 强制 |
+| - | - | - |
+| WHERE | `"id=$id"` | `WHERE id = ?` |
+| LIKE | `LIKE "%$kw%"` | `LIKE CONCAT('%', ?, '%')` |
+| IN | 手动拼 `IN(1,2,3)` | 动态占位符 `IN(?,?,?)` |
+| ORDER BY | 直接拼用户输入 | 白名单字段映射 |
+| LIMIT | 直接拼字符串 | 转 int 后限制最大值 |
+
+### 第六步:事务、锁和批处理
+
+- 多表写入必须显式事务:`begin → write → commit`,异常必须 rollback
+- 单事务影响行数超过 10000 时必须分批
+- 批量导入/导出/修复数据必须使用 `Init → Step → Poll` 渐进式防卡死架构
+- 高并发扣减库存/余额类场景必须说明锁策略:行锁、乐观锁或唯一约束
+- 禁止无 WHERE 的 UPDATE / DELETE
+
+### 第七步:慢查询诊断
+
+慢查询必须先拿证据:
+
+1. 原始 SQL
+2. 参数样例
+3. 表行数
+4. `EXPLAIN` 结果
+5. 相关索引
+
+诊断重点:
+
+| EXPLAIN 字段 | 风险信号 |
+| - | - |
+| type | `ALL` / `index` 需警惕 |
+| rows | 扫描行数远大于返回行数 |
+| key | 未命中预期索引 |
+| Extra | `Using filesort` / `Using temporary` |
+
+优化顺序:改查询条件 → 调整索引 → 改分页/统计方式 → 拆表/缓存。不得先上缓存掩盖 SQL 问题。
+
+### 第八步:迁移、备份与回滚
+
+DDL/数据修复必须输出:
+
+- 执行前 SELECT 验证
+- 备份方案
+- 执行 SQL
+- 影响行数预估
+- 回滚 SQL 或恢复方案
+- 执行窗口和风险提示
+
+大表 ALTER 必须提示使用在线 DDL 或 `pt-online-schema-change`,禁止业务高峰直接执行。
+
+### 第九步:记录到项目记忆
+
+将以下内容沉淀:
+
+- 表结构和字段约定
+- 索引设计理由
+- 关键 SQL 和参数绑定方式
+- 事务边界和回滚策略
+- 慢查询诊断结论
+- CMS 表前缀和特殊表规则
+
+## 输出格式
+
+```markdown
+# MySQL数据库方案
+
+## 1. 场景判断
+- 类型: [建表/查询/优化/迁移/修复]
+- 数据规模: [当前/预估]
+- MySQL版本: [版本或假设]
+
+## 2. 表结构/SQL方案
+[DDL 或参数化 SQL]
+
+## 3. 索引设计
+| 查询路径 | 推荐索引 | 字段顺序理由 | 覆盖查询 | 写入成本 |
+|----------|----------|--------------|----------|----------|
+
+## 3.1 索引治理
+- 冗余索引:
+- 删除/合并建议:
+- 回滚SQL:
+
+## 4. 安全与事务
+- 参数绑定:
+- 事务边界:
+- 锁策略:
+
+## 5. 性能验证
+- EXPLAIN:
+- 预期扫描行数:
+- 风险:
+
+## 6. 迁移/回滚
+- 备份:
+- 执行:
+- 回滚:
+
+## 7. 验证证据
+- 测试SQL:
+- 预期结果:
+- 影响行数:
+```
+
+## 质量标准
+
+- 任何 SQL 涉及外部输入时必须参数绑定
+- 任何 UPDATE / DELETE 必须带 WHERE,并说明执行前 SELECT
+- 建表必须说明字符集、引擎、主键、时间字段、必要索引
+- 索引必须对应具体查询路径,不能无理由添加
+- 联合索引必须说明字段顺序理由:等值、区分度、范围、排序、覆盖
+- 高频查询必须提供 EXPLAIN 验证,说明 `key/type/rows/Extra`
+- 新增索引必须评估写入成本、磁盘成本和冗余索引
+- 删除/合并索引必须提供依赖 SQL、风险和回滚 SQL
+- 金额字段禁止 float
+- 慢查询必须基于 `EXPLAIN` 或明确说明缺少执行计划
+- DDL/数据修复必须有备份和回滚说明
+- 大批量任务必须采用 Init → Step → Poll
+- CMS 场景必须说明表前缀和官方数据访问层
+
+## 失败回退机制
+
+| 步骤 | 失败场景 | 回退策略 | 重试次数 | 最终处理 |
+| - | - | - | - | - |
+| 第一步:识别数据库任务类型 | 需求不清 | 按建表/查询/优化/迁移四类询问用户 | 1 | 输出假设并标注待确认 |
+| 第二步:确认版本、字符集和会话设置 | MySQL版本未知 | 默认 MySQL 8.0,兼容 5.7 写法 | 1 | 标注版本假设 |
+| 第三步:表结构设计 | 字段含义不足 | 输出最小字段集并列待确认字段 | 1 | 不生成破坏性 DDL |
+| 第四步:索引设计 | 缺查询路径 | 只给必要主键/唯一索引 | 1 | 提示补充查询条件 |
+| 第五步:SQL安全与参数绑定 | 框架访问层未知 | 使用 PDO 预处理示例 | 1 | 标注需替换为项目 ORM/CMS API |
+| 第六步:事务、锁和批处理 | 一致性要求未知 | 按保守事务方案设计 | 1 | 标注并发风险 |
+| 第七步:慢查询诊断 | 缺 EXPLAIN | 先输出需采集命令和初步假设 | 1 | 不宣称已完成优化 |
+| 第八步:迁移、备份与回滚 | 无备份条件 | 阻止破坏性操作 | 0 | 要求先备份 |
+| 第九步:记录到项目记忆 | 项目记忆不可用 | 输出本地记录清单 | 1 | 标注未沉淀 |
+
+## 关联 reference
+
+- **cms-development**(CMS二次开发)- PHP+MySQL CMS 场景必须联动 CMS 表前缀、官方数据访问层和缓存规则
+- **code-generation**(代码生成)- 生成 PHP/后端代码时按本技能的 SQL 安全、事务和类型映射执行
+- **code-review**(代码审查)- 审查 SQL 注入、慢查询、事务边界、DDL 风险和索引滥用
+- **bug-diagnosis**(Bug诊断)- 数据库报错、死锁、慢查询、表不存在、字符集问题优先调用本技能
+- **tech-selection**(技术选型)- 数据库版本、存储引擎、缓存和搜索方案选型
+- **website-project**(网站项目总控)- 建站项目中的数据模型、内容表、表单提交、统计和上线迁移
+- **task-decomposition-and-execution**(任务拆解与执行)- 大型数据库变更按 Wave 拆分并逐步验证
+- **project-memory-management**(项目记忆管理)- 沉淀表结构、索引、SQL 约定和迁移记录

+ 183 - 0
.claude/skills/dev-expert/references/performance-benchmark.md

@@ -0,0 +1,183 @@
+# 性能基准测试
+
+## 输入要求
+
+1. **目标代码路径**(必填):被测函数/模块/脚本
+2. **性能指标**(必填):耗时 / 内存 / 吞吐量 / QPS / 启动时间,至少其一
+3. **阈值**(可选):目标上限,如"单次调用 < 200ms";不提供则仅输出基准数据
+4. **对比基线**(可选):优化前的基准数据,用于 A/B 对比
+
+## 执行流程
+
+### 第一步:识别性能触发面
+
+扫描代码,定位性能关键路径:
+
+- 循环体内重计算(正则编译、字符串拼接、重复 I/O)
+- 批量操作(大批次读文件、批量 SQL、批量网络请求)
+- 数据结构选择(list vs set vs dict 查重、字符串拼接 vs join)
+- 缓存缺失(重复调用同一开销大的函数 / 重复查询同一数据)
+- 数据库查询(N+1、缺索引、全表扫描 — 发现后跳转 `mysql-database`)
+
+#### 自动化扫描档位
+
+当用户提供文件或目录路径时,先执行可复现的静态扫描,再进入人工分析;当用户只粘贴代码片段且没有可访问路径时,跳过工具扫描并明确标注"仅基于片段审查"。
+
+| 扫描档位 | 触发条件 | 扫描范围 | 输出要求 |
+| - | - | - | - |
+| 快速扫描 | 单个文件、单个函数、短代码片段 | 当前文件或片段中的循环、I/O、SQL、缓存 | 输出命中位置和疑似反模式 |
+| 深度扫描 | 目录、模块、项目级性能排查 | 入口、循环、数据库访问、网络调用、缓存、异步任务 | 输出扫描摘要、热点候选和后续验证路径 |
+
+推荐扫描模式:
+
+| 扫描项 | 参考模式 | 风险 |
+| - | - | - |
+| 循环内数据库调用 | `for` / `foreach` / `while` 附近出现 `query` / `select` / `find` / `where` | N+1 查询 |
+| 循环内外部请求 | 循环附近出现 `fetch` / `axios` / `requests` / `http` / `RestTemplate` | N+1 API 调用 |
+| 同步阻塞 I/O | `readFileSync` / `writeFileSync` / 同步文件读写 / 高并发路径阻塞调用 | 阻塞 I/O |
+| 无上限数据拉取 | `.all()` / `findAll()` / `SELECT *` / 无 `LIMIT` 的列表查询 | 内存和响应时间失控 |
+| 缓存无界增长 | 全局 Map/dict/Object/静态集合持续写入且无 TTL/容量限制 | 内存泄漏 |
+| 索引失效 | `DATE(column)` / `LIKE '%xxx'` / 字段隐式类型转换 | 慢查询 |
+
+扫描结果只能作为嫌疑点;交付结论必须继续通过 benchmark、profile、EXPLAIN、日志或人工复核验证。
+
+#### 分层分析深度
+
+根据问题规模选择分析层级,避免简单问题过度分析,也避免复杂问题只给浅层建议:
+
+| 层级 | 触发条件 | 分析内容 | 输出形态 |
+| - | - | - | - |
+| L0 轻量分析 | 单条 SQL、单个函数、少于 30 行的小片段 | 直接复杂度、明显 I/O、单点风险 | 简短结论 + 1 到 3 个建议 |
+| L1 标准分析 | 多函数、循环 + DB、常规重构、30 到 200 行代码 | 复杂度、I/O、边界条件、数据量影响 | 热点表格 + 修复方案 |
+| L2 深度分析 | 多文件、架构级优化、线上瓶颈、超过 200 行或涉及并发 | L1 全部 + 并发、内存、缓存、扩展性、回滚风险 | 分阶段诊断和验证计划 |
+
+#### 硬性性能反模式
+
+| 反模式 | 典型信号 | 严重度 | 最小修复方向 |
+| - | - | - | - |
+| N+1 查询 | 循环内数据库查询或外部 API 调用 | 严重 | 批量查询、预加载、JOIN、聚合、一次性拉取后内存分组 |
+| 同步阻塞 I/O | 高并发请求路径内同步文件、网络或重 CPU 操作 | 严重 | 异步化、后台任务、连接池、队列、流式处理 |
+| 内存泄漏风险 | 无界缓存、监听器未清理、静态集合持续增长 | 高 | TTL、容量限制、LRU、生命周期清理 |
+| 无上限数据拉取 | 列表查询无分页、无最大 limit、全表拉取 | 高 | 强制分页、游标、批处理、最大 page size |
+
+### 第二步:选择优化策略
+
+识别反模式后,按问题性质选择策略;没有实测基线时只能描述"预期收益"或"假设收益",不得声称已提升。
+
+| 策略 | 适用场景 | 风险控制 |
+| - | - | - |
+| 缓存优先 | 高频读取、变更不频繁、可接受短暂不一致 | 明确 TTL、失效时机、穿透/击穿/雪崩防护 |
+| 异步处理 | 耗时超过用户等待阈值的非核心副作用 | 明确幂等、重试、死信、失败告警和最终一致性 |
+| 索引优化 | WHERE/JOIN/ORDER BY 慢查询 | 必须提供 EXPLAIN,评估写入成本和回滚方案 |
+
+优化建议输出需包含:问题位置、当前路径、方案、预期收益依据、验证命令、回滚方式。
+
+### 第三步:选用测量工具
+
+按语言和场景选择:
+
+| 语言 | 工具 | 适用场景 |
+| - | - | - |
+| Python | `cProfile` / `timeit` / `pytest-benchmark` | 函数级耗时、逐行热点 |
+| Node.js | `console.time` / `benchmark.js` / `clinic` | 火焰图、事件循环延迟 |
+| Go | `go test -bench` / `pprof` | CPU/内存 profile |
+| Java | JMH | 微基准测试 |
+| PHP | `microtime()` / Xdebug profiler | 函数耗时 |
+
+**首选策略**:若项目已有测试框架,优先用其内置 benchmark(如 pytest-benchmark、go test -bench),降低接入成本。
+
+### 第四步:执行测量并采集数据
+
+- 跑 3 次取中位数,标注冷/热启动差异
+- 记录环境:CPU 型号/核心数、内存、OS、Python 版本
+- 内存测试记录峰值 RSS(`tracemalloc` / `memory_profiler` / `process.memory_info()`)
+- 输出原始数据 + 整理后表格
+
+### 第五步:生成瓶颈报告
+
+对超出阈值的热点逐条分析:
+
+- 热点代码位置(文件:行号)
+- 当前耗时 / 内存占用
+- 根因分析(为什么慢)
+- 优化建议(具体方案 + 预期收益)
+
+### 第六步:优化前后对比(如有基线)
+
+| 指标 | 优化前 | 优化后 | 提升 |
+| - | - | - | - |
+| 耗时 | X ms | Y ms | Z% |
+| 内存 | X MB | Y MB | Z% |
+| 调用次数 | X | Y | Z% |
+
+### 第七步:记录到项目记忆
+
+性能基准测试完成后,将以下内容沉淀到项目记忆(参见 `project-memory-management.md`):
+
+- **性能基线数据**:中位数/P95/P99/峰值内存,作为后续回归对比依据
+- **热点位置与根因**:文件:行号 + 耗时占比 + 优化建议
+- **优化前后对比**:提升幅度和验证证据
+- **benchmark 脚本路径**:纳入测试套件持续运行
+- **性能陷阱**:本轮发现的性能反模式(如 N+1 查询、O(n²) 循环、不必要的序列化)
+
+## 输出格式
+
+````
+## 性能基准测试报告
+
+**目标代码**:[函数/模块名]
+**测试环境**:[OS / CPU / 内存 / Python 版本]
+
+## 基准数据
+
+| 指标 | 中位数 | P95 | P99 | 峰值内存 |
+|------|--------|-----|-----|---------|
+| [指标1] | [值] | [值] | [值] | [值] |
+
+## 热点分析
+
+| 位置 | 耗时占比 | 根因 | 优化建议 | 预期收益 |
+|------|---------|------|---------|---------|
+| [文件:行号] | X% | [原因] | [方案] | ~Y% |
+
+## 优化前后对比(如有基线)
+
+| 指标 | 优化前 | 优化后 | 提升 |
+|------|--------|--------|------|
+
+## 测试脚本
+
+```[语言]
+[可复现的 benchmark 代码]
+````
+
+## 质量标准
+
+- 必须跑 3 次以上取中位数,单次结果不可作为结论
+- 必须标注测试环境(硬件 + 软件版本),否则数据不可复现
+- 热点分析必须有具体代码位置(文件:行号),不得只说"某个循环慢"
+- 优化建议必须给出可操作的方案,不得只说"优化一下"
+- 不得在无基线对比的情况下声称"显著提升"
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+|------|---------|---------|---------|------------------|
+| 第一步:识别触发面 | 代码量过大无法定位关键路径 | 按函数分批,标注"仅部分路径" | 1 | 缩小范围到用户指定的入口函数 |
+| 第二步:选择优化策略 | 无实测基线或策略不明 | 先跑基线,标注"预期收益/假设收益" | 1 | 输出候选策略与权衡,由用户决策 |
+| 第三步:选用测量工具 | 工具不可用或安装失败 | 降级到语言内置 time 打点 | 1 | 输出手动计时方案,标注精度受限 |
+| 第四步:执行测量 | 环境差异导致数据波动 > 20% | 增加采样次数到 7 次,输出方差 | 2 | 标注"环境不稳定",以区间范围呈现 |
+| 第五步:生成瓶颈报告 | 热点根因无法在静态分析中确定 | 输出热点位置 + 耗时占比,标注"根因待确认" | 1 | 建议用户补充 profiler 火焰图 |
+| 第六步:优化前后对比 | 无优化前基线 | 仅输出当前基准,标注"无基线对比" | — | 正常输出 |
+| 第七步:记录到项目记忆 | 项目记忆系统不可用 | 输出性能基线数据到本地文件 | 1 | 标注"基线未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **code-review**(代码审查)— 发现潜在性能问题后,用本技能定量验证
+- **code-generation**(代码生成)— 生成代码后可用本技能跑基准测试验证性能
+- **refactoring**(重构建议)— 重构前跑基线,重构后跑对比,量化重构收益
+- **mysql-database**(MySQL数据库)— SQL 性能问题跳转 `mysql-database` 做 EXPLAIN / 索引分析
+- **test-generation**(测试用例生成)— benchmark 脚本应纳入测试套件持续跑基准
+- **software-project**(软件项目总控)— 软件项目上线前性能验收,提交基准数据作为交付物
+- **website-project**(网站项目总控)— 网站项目的性能指标、部署前压测和上线后监控基准
+- **project-memory-management**(项目记忆管理)— 记录性能基线数据,作为后续回归对比依据

+ 144 - 0
.claude/skills/dev-expert/references/project-knowledge-graph.md

@@ -0,0 +1,144 @@
+# 项目知识图谱(代码结构认知层)
+
+## 输入要求
+
+1. **工作区根目录 `{PROJECT_ROOT}`**(必填):项目代码根,图谱按工作区独立存储于 `{PROJECT_ROOT}/.ai-memory/knowledge-graph/`,与会话生命周期无关、可随时重建。
+2. **改动文件清单**(P1 规划前置可选):跨模块改动时传入"本次拟改文件集",用于查"改动闭包"而非全量。
+3. **查询入口**(P3 显式查询可选):`file`(相对路径)或 `symbol`(类名/函数名,经 symbols.json 解析)。
+
+## 定位与边界
+
+- **受众**:图谱唯一受众是 agent 开发时借全局视角理解项目;**不生成 mermaid**(人类可视化源,agent 不加载,纯冗余)。人类兜底为 `graph.md`(文本邻接表,agent 不读)。
+- **与"任务依赖图"区分**:本图谱是**代码结构依赖(持久、跨会话)**;`task-decomposition` / `software-project` / `refactoring` 里的"依赖图"是**任务执行依赖(临时、本次任务内)**。前者称"代码结构图谱",后者称"任务依赖",命名严格拉开。
+- **与现有机制互补**:① 延迟加载协议——查图谱替代全仓扫;② `project-memory`——结构层 vs 决策层,互引不重叠;③ 不进记忆蒸馏、不参与 Git 团队共享。
+- **纯 agent 加速器,非验证替代品**:正常改动靠它快定位;动刀前 grep 复核那一下不能省(见硬门禁)。
+
+## 执行流程
+
+### 第一步:触发规模门控
+
+| 场景 | 行为 |
+| - | - |
+| 简单任务 / 单文件(L0) | **不触发**,图谱是噪声 |
+| 跨模块改动 / 重构 / 审计 / 接手陌生项目(≥3 文件) | 主动查图谱(先走新鲜度校验) |
+| 显式 `@project-knowledge-graph` 或"画依赖图/模块关系" | 构建 / 查看 / 查询 |
+| 文件变更后下次查询 | 哈希比对自动判定过期 → 提示或增量重建 |
+
+### 第二步:构建 / 冷启动(工具侧抽取,token ≈ 0)
+
+调用 `scripts/build_graph.py`(纯脚本写盘,不进 LLM 上下文):
+
+```bash
+# 全量构建
+python scripts/build_graph.py --root {PROJECT_ROOT} --rebuild
+
+# 查时若图不存在 → 冷启动:先全量构建再查(见第三步)
+
+# 内置 E2E 自检(删文件无悬挂边 + 改文件过期判定,不改交付物)
+python scripts/build_graph.py --root {PROJECT_ROOT} --selftest
+```
+
+- **抽取技术**:v1 默认纯正则静态抽取(零依赖、跨平台、秒级、多语言通用,无 AST/tree-sitter);LSP 仅做可用性探测(`detect_lsp` 记 `lsp_available`),抽取仍走正则(语义增强为可选留口,当前未实现)。结果写 `meta.json.lsp_available`。
+- **语言范围**:PHP / JS(一等支持);Java / Python / TS / Go 同样建图(通用正则粗边),LSP 可用仅记录、不改变抽取方式。
+- **原子写**:graph.json / meta.json / symbols.json 均先写 `.tmp` 再 `rename` 覆盖,避免半文件。
+- **抽取自校验(准确性硬保障,构建阶段强制)**:
+  1. **边目标反查剔除悬空边**:每条 include/require/import 边解析为绝对路径后校验目标是否存在;不存在 → 判悬空边,**不写入有效边集**,仅记入 `meta.json.dangling_edges`(含源:行号 + 解析值 + 所用 base)。base 解析规则:源文件目录优先;无 `./`/`../` 前缀且非绝对 → 回退 include_path + 工作区根;两路都解析不到才判悬空。
+  2. **符号孤儿仅告警不裁决**:类/函数节点反查 `symbols.json` 找不到定义 → 标 `orphan` 提示,**不自动删节点/边**(symbols 自身可能漏抽导致误标);真正剔除须查后动作 grep 复核确认。
+  3. **覆盖率自检报告**:写 `meta.json.accuracy_report`(`edges_total` / `edges_valid` / `edges_dangling` / `symbols_*` / `static_coverage` / `lsp_available`);构建日志输出 `[GRAPH-ACCURACY] 有效边 X / 悬空 Y(已剔除)/ 覆盖率 Z% / LSP: php`。
+
+### 第三步:查询接口与子图裁剪(核心,决定 agent 怎么用图谱)
+
+```bash
+python scripts/build_graph.py --root {PROJECT_ROOT} --query <file|symbol> [--direction up|down|both] [--depth N]
+```
+
+- `--query`:`file` 直接定位;`symbol` 经 `symbols.json` 解析为文件:行号再查(无索引则打印"符号索引缺失"并返回空子图,不扫描全图);多个入口用逗号分隔(如 `--query a.php,b.php`),返回合并子图;同名符号歧义 → 返回候选列表(每个含文件:行号 + 所属 module),由模型/用户选定其一。
+- `--direction`:上游依赖(谁依赖它)/ 下游(它依赖谁)/ 双向。
+- `--depth N`:**默认 2 跳**——防止一次查询把半个项目读进上下文,守住"省 token"承诺。
+- **环检测**:遍历维护已访问集合,遇已访问节点折叠标记 `cycle:true` 不再展开,杜绝 A→B→A 膨胀/死循环;输出附 `[GRAPH-CYCLE] 检测到 N 个环`。
+- **节点预算硬上限**:子图节点数超 `MAX_NODES=80` 即截断(防枢纽模块 2 跳爆炸,如 `connect.php`)。
+- **输出**:仅返回 N 跳内 `{nodes, edges, cycle}` 紧凑 JSON 片段(预计 2K–10K token)进入上下文。
+
+### 第四步:新鲜度校验(最关键,防失效误导)
+
+**唯一判定机制:查时比对哈希**(不依赖 PostToolUse 脏标记,避免双套逻辑分裂):
+
+- 每次**查图谱前**比对 `meta.json` 文件清单 vs 当前工作区:先用 **mtime 粗筛**(mtime 未变 → 直接判未过期),仅变更文件重算**内容哈希精筛**。
+- 过期 → 输出 `[GRAPH-STALE] 图谱可能过期(N 个文件变更),正在增量重建…` 并触发重建(v1 增量=全量重抽,统一覆盖增/删/改/重命名四类);或直接提示用户重建。
+- **冷启动**:`meta.json`/产物缺失(首次使用或图被误删)→ 输出 `[GRAPH-STALE] 图谱可能过期(N 个文件变更/产物缺失),正在增量重建…` 并当场全量构建;构建完成再查。若自主度低或构建耗时过长 → 降级为"临时 grep 局部依赖"并在交付标注"图谱未构建,已用临时分析替代",不阻断主任务。
+
+### 第五步:查后动作规范(C,决定"改动更稳"落地)
+
+模型拿到子图后必须显式转化为执行动作,而非"看了眼":
+
+1. 若入口为"待改模块",列出子图内**所有上游依赖方(谁依赖它)** → 纳入改动影响评估范围。
+2. 对受影响文件清单,**在修改阶段一并改、在验证阶段一并跑 lint/断言**(防漏改依赖方)。
+3. 输出"影响面摘要"给用户(改 A 会影响 B/C,已纳入范围),作为交付"已知限制/范围"的一部分。
+
+### 第六步:硬门禁(双保险底线,图谱不准时也不轻信交付)
+
+自校验保证"不准的边不进图"(第二步),但漏抽(动态依赖)自校验发现不了,须靠门禁兜底——以下任一条不满足,**不得交付,且 SELF-AUDIT 直接判不通过**:
+
+- **G1 grep 冲突以 grep 为准**:图谱影响面结论与 grep 实测不符 → 一律以 grep 为准,图谱仅作提示,不得用图谱覆盖 grep 结果。
+- **G2' 图谱影响面须附 grep 实测证据**:图谱给出的影响面清单,**必须**经独立 grep 复核(对清单内每个文件确认真实依赖);交付物须**附 grep 原始输出片段**(如 `grep -rn "class X" 命中 N 处 @ file:line`),SELF-AUDIT 核对证据存在且覆盖清单全部文件;**无证据即判不通过,卡在交付前**(与"无验证证据=未完成"红线同源)。agent 不能靠自评绕过谎报。
+- **G3 不可逆操作单独确认**:删 / 重命名 / DDL 等不可逆操作,**不单凭图谱结论**执行;必须独立 grep 复核真实依赖 + 走用户确认闸门(复用既有"破坏性操作默认取消"铁律)。
+
+## 作用点锚定(实施时写入 SKILL.md Step 2 / Step 3,不靠 agent 猜测)
+
+| 作用点 | 触发条件 | 钉到 Step | 查什么 | 产出落哪 |
+| - | - | - | - | - |
+| **P1 规划前置依赖分析** | 复杂任务(≥3 文件 / 跨模块)进方案阶段 | **Step 2 规划(PLAN-GATE 前)** | **改动文件集闭包**(`--query <改动文件1,改动文件2,…> --direction both --depth 3`),非项目全量 | 喂 `_plan.md` 依赖矩阵,替代临时 grep |
+| **P2 执行前局部影响面** | Step 3 要改具体模块 | **Step 3 执行(改码前,先新鲜度校验)** | 该模块 2 跳上游(`--query <模块> --direction up --depth 2`) | 喂查后动作规范(受影响文件一并改+验证) |
+| **P3 显式请求** | 用户 `@project-knowledge-graph` 或"画依赖图/模块关系" | 任意 Step,用户直接要 | 按用户入口 | 构建/查看/查询 |
+| **不触发** | 简单任务 / 单文件(L0) | — | — | 图谱是噪声,跳过 |
+
+- **P1 vs P2 不冲突**:P1 全量广度(喂 plan),P2 局部深度(喂执行),互补。
+- **默认链路**:`复杂任务 → Step 2 前 P1 查全量依赖 → 写 plan 依赖矩阵 → Step 3 改某模块前 P2 查上游 → 查后动作规范 → 执行`。
+
+## 存储 Schema
+
+```
+{PROJECT_ROOT}/.ai-memory/knowledge-graph/
+├── graph.json      # 节点 + 边(结构化,机器读,agent 唯一消费源)
+├── meta.json       # 构建时间、工具版本、文件清单哈希、范围配置、覆盖率、accuracy_report、script_hash
+├── graph.md        # 文本邻接表(人类可读兜底,agent 不加载)
+└── symbols.json    # 符号索引:类名/函数名 → 文件:行号(支撑 --query <symbol>)
+```
+
+- **节点**:file(默认粒度)/ module(目录级聚合单元,按源码根下一级业务目录推导,v1 不做 namespace 细聚)。
+- **边类型**:`include`(含 require/require_once/include_once,统一归 include)`use`(命名空间 use)`autoload`(new \Ns\Class / \Ns\Class:: 自动加载)`extends`(类继承)`template`({include file=}/template())`tpimport`(ThinkPHP import()/vendor()/Loader::import())`import`(非 PHP 语言模块导入)`cssimport`(CSS @import)`asset`(HTML link/script 资源)`calls`(跨文件调用)。注:`implements`/`require` 非独立边类型——`require` 并入 `include`,`implements` 作为类属性记录、不单独成边。
+- **敏感边约束**:`calls` 边只记跨文件/跨模块调用,过滤标准库/框架内置/密钥读取等内部调用,避免泄露敏感路径且降噪。
+- **不参与记忆蒸馏**:图谱基于代码、随代码重建,不纳入"超 30 天蒸馏/删除"。
+- **多工作区切换**:会话切换 `{PROJECT_ROOT}` 时图谱上下文随之切换,互不干扰。
+
+## 输出格式
+
+- **agent 消费三件套**:`graph.json`(节点+边,唯一结构化输入)+ `meta.json`(构建/覆盖率/新鲜度)+ `symbols.json`(符号索引)。一律走 `--query` 查询接口返回裁剪子图 JSON,绝不读全量文件。
+- **人类兜底**:`graph.md`(文本邻接表),仅供人类开发者偶尔查看;**agent 不加载**。
+- **明确排除**:不生成 `graph.mmd` / mermaid;不在 README/FAQ 展示渲染图。
+
+## 质量标准
+
+1. 构建阶段 LLM token ≈ 0(输出写盘不进上下文)。
+2. `--query` 返回 N 跳子图 token < 10K(深度默认 2 + MAX_NODES=80 上限兜底)。
+3. 自校验生效:悬空边不进有效边集、孤儿仅告警不裁决、构建日志输出 `[GRAPH-ACCURACY]`。
+4. 增量四类覆盖:删文件后图谱无悬挂边;改文件后查图谱触发过期判定(STALE)。
+5. 降级可用:构建失败不阻断主任务,回退临时 grep + 交付标注"图谱不可用"。
+
+## 失败回退机制
+
+| 失败类型 | 回退动作 |
+| - | - |
+| 构建脚本缺失 Python / 权限 / 环境异常 | 降级为"临时 grep 局部依赖",交付标注"图谱不可用,已用临时分析替代",不阻断主任务 |
+| 图谱过期但重建超时 | 同降级:临时 grep + 标注 |
+| 符号索引缺失(--query <symbol> 无结果) | 返回空子图 + 提示"符号索引缺失";仍无果 → 临时 grep |
+| 图谱影响面与 grep 冲突(G1) | 一律以 grep 为准,图谱仅作提示 |
+| 动刀前未 grep 复核(G2') | SELF-AUDIT 判不通过,卡交付前,补 grep 证据 |
+| 不可逆操作仅凭图谱(G3) | 独立 grep 复核 + 用户确认闸门 |
+| 子图超 80 节点(枢纽模块) | 查询自动截断 + 提示缩小入口/降 depth |
+
+## 常见陷阱
+
+- **动态拼路径漏抽**:`require($base.'/class/'.$c.'.php')` / `import(resolve(dirname))` 正则抽不出,属通用边界(非某框架特有),README 须明示"不宣称 100% 结构还原";漏抽兜底在查后动作 grep 复核 + 用户反馈纠偏闭环。
+- **图谱≠真相**:漏抽自校验无真相可比对,不会报错也不会补全,故门禁 G1/G2'/G3 是最后底线。
+- **绝对路径 target 未归一化**:构建时若源用 `ECMS_PATH` 等常量拼绝对路径,edges 会出现绝对路径 target(如 `j:/devx-sim/e`),查询时按相对路径匹配会失配——后续可考虑在 build 内归一化(可选优化,不影响现有相对路径边)。

+ 482 - 0
.claude/skills/dev-expert/references/project-memory-management.md

@@ -0,0 +1,482 @@
+# 项目记忆管理 -- 跨会话项目记忆沉淀
+
+## 输入要求
+
+1. **记录类型**(必填):session总结 / 决策记录 / 规范沉淀 / 已知问题清单 / 换机交接 / 记忆恢复
+2. **内容详情**(必填):具体要记录的信息
+3. **项目标识**(可选):项目名称或标识符
+
+## 记忆文件存储位置
+
+记忆沉淀和恢复遵循"**项目记忆跟随项目,用户偏好跟随用户**"原则:L3/L2/L1 记忆存储在项目根目录下的 `.ai-memory/` 目录(跟随项目迁移、可团队共享),L4 用户级记忆存储在用户主目录(跨项目复用)。**严禁硬编码任何绝对路径**,确保跨 IDE(Trae CN / Trae / Marvis / VSCode 等)可用。
+
+### 路径根目录探测协议
+
+LLM 在首次写入或读取记忆前,必须按以下优先级链探测两个根目录:
+
+#### `{PROJECT_ROOT}` 项目根目录(L3/L2/L1 记忆使用)
+
+| 优先级 | 探测源                        | 适用场景                               | 示例                             |
+| ------ | ----------------------------- | -------------------------------------- | -------------------------------- |
+| 1      | 环境变量 `PROJECT_ROOT`       | 用户显式指定,跨 IDE 通用              | `set PROJECT_ROOT=D:\my-project` |
+| 2      | Git 仓库根目录                | 已纳入 Git 管理的项目                  | `git rev-parse --show-toplevel`  |
+| 3      | IDE 当前工作区根目录          | IDE 打开的项目目录                     | 工作区路径 / cwd                 |
+| 4      | 询问用户并写入 `PROJECT_ROOT` | 以上均未命中时,强制询问,不得自行假设 | 用户确认后写入环境变量           |
+
+#### `{USER_PROFILE}` 用户主目录(L4 记忆使用)
+
+| 优先级 | 探测源                  | 适用场景                 | 示例                                            |
+| ------ | ----------------------- | ------------------------ | ----------------------------------------------- |
+| 1      | 环境变量 `USER_PROFILE` | 用户显式指定             | `set USER_PROFILE=C:\Users\username`            |
+| 2      | 系统用户主目录          | 默认场景,跨平台自动识别 | Windows: `%USERPROFILE%` / macOS/Linux: `$HOME` |
+| 3      | 询问用户                | 以上均未命中时,强制询问 | 用户确认后写入环境变量                          |
+
+### 记忆文件相对路径
+
+探测到根目录后,4 层记忆文件按以下路径组织:
+
+| 层级      | 路径                                                                     | 用途                            | 跟随 |
+| --------- | ------------------------------------------------------------------------ | ------------------------------- | ---- |
+| L4 用户级 | `{USER_PROFILE}/.ai-memory/user_profile.md`                              | 跨项目用户偏好、技术栈、禁忌    | 用户 |
+| L3 项目级 | `{PROJECT_ROOT}/.ai-memory/project_memory.md`                            | 项目规则、约束、约定、教训      | 项目 |
+| L3 交接级 | `{PROJECT_ROOT}/.ai-memory/handoff.md`                                   | 未完成任务的换机/换 IDE 交接    | 项目 |
+| L2 会话级 | `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/session_memory_{session-id}.jsonl` | 单次会话的任务/TODO/相关文件    | 项目 |
+| L1 工作级 | `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/daily.md`                          | 当日逐条操作日志(append-only) | 项目 |
+| L1 对话级 | `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/topics.md`                         | 当日会话主题摘要                | 项目 |
+
+**路径变量解析**:
+
+- `{PROJECT_ROOT}`:按上述探测协议动态获取,禁止硬编码
+- `{USER_PROFILE}`:按上述探测协议动态获取,禁止硬编码
+- `{YYYYMMDD}`:当日日期,目录不存在时由记忆系统自动创建
+- `{session-id}`:会话标识,格式为 `YYYYMMDD-HHmm`(本次会话首次进入 Step 1 的本地时间戳,到分钟)。**唯一生成/存储约定以 `references/execution-safety.md`「session_memory 标识(id)生成规则」为准**:写进当日 `daily.md` 顶部 `session-id: {YYYYMMDD-HHmm}` 行作为真相源,新会话先读该行再定位 `session_memory_{session-id}.jsonl`。
+
+### `.ai-memory/` 目录结构示例
+
+```
+{PROJECT_ROOT}/
+├── .ai-memory/                          # 项目记忆根目录(跟随项目)
+│   ├── project_memory.md                # L3 项目级:规则、约束、教训
+│   ├── handoff.md                       # L3 交接级:未完成任务恢复点
+│   ├── 20260705/                        # L2/L1 按日期组织
+│   │   ├── topics.md                    # L1 当日会话主题摘要
+│   │   ├── daily.md                     # L1 当日逐条操作日志(append-only)
+│   │   ├── session_memory_20260813-1430.jsonl # L2 单次会话记录(id=YYYYMMDD-HHmm)
+│   │   └── session_memory_20260813-1510.jsonl
+│   └── 20260706/
+│       └── ...
+├── .gitignore                           # 建议 .ai-memory/daily/ 加入忽略
+└── ...(项目其他文件)
+```
+
+### 版本控制策略
+
+| 路径                                    | 是否提交 Git | 原因                                       |
+| --------------------------------------- | ------------ | ------------------------------------------ |
+| `.ai-memory/project_memory.md`          | ✅ 提交      | 项目规则和约定,团队共享,版本追溯         |
+| `.ai-memory/handoff.md`                 | ✅ 提交      | 换电脑/换 IDE 时恢复未完成任务             |
+| `.ai-memory/{YYYYMMDD}/topics.md`       | ❌ 忽略      | 当日会话摘要,含个人上下文,不团队共享     |
+| `.ai-memory/{YYYYMMDD}/daily.md`        | ❌ 忽略      | 当日逐条操作日志,含个人上下文,不团队共享 |
+| `.ai-memory/{YYYYMMDD}/session_*.jsonl` | ❌ 忽略      | 单次会话详情,含个人上下文,不团队共享     |
+| `{USER_PROFILE}/.ai-memory/`            | 不适用       | 用户主目录,不在项目仓库内                 |
+
+**`.gitignore` 建议添加**:
+
+```
+.ai-memory/*/
+!.ai-memory/project_memory.md
+!.ai-memory/handoff.md
+```
+
+### 探测执行规则
+
+首次执行时输出探测过程:`[MEMORY-PROBE] PROJECT_ROOT: 优先级X→命中/未命中; USER_PROFILE: 优先级X→命中/未命中`。命中后缓存当前会话。优先级1-3均未命中时禁止自行创建目录,必须询问用户:`未找到项目根目录,请指定 PROJECT_ROOT 路径(如 D:\my-project)`。
+
+### 写入规则
+
+- 探测到根目录后,写入前必须确认 `.ai-memory/` 目录存在,不存在时先创建
+- 写入必须使用 UTF-8 无 BOM 编码,禁止使用 PowerShell 的 `Set-Content` / `Out-File`
+- L1/L2 每日一个目录,L3 每项目一个文件,L4 每用户一个文件
+- Windows 环境下路径分隔符为 `\`,macOS/Linux 为 `/`,写入时按当前系统规范化
+
+### 即时写入规则(写后即记)
+
+`daily.md` 采用 **append-only** 模式,在每次实质性操作完成后**立即追加**,不经用户确认、不等待会话结束。写入触发条件、写入格式和运行位置见主 SKILL.md「Step 0: 工作记忆加载 → 写后即记协议」。
+
+**实现规则**:
+
+1. 每次追加前仅读取 `daily.md` 末尾 20 行确认已有记录(避免全量读取浪费上下文),然后直接 append
+2. 写入使用 UTF-8 无 BOM 编码,Windows 换行符 `\r\n`
+3. 若 `daily.md` 不存在,先创建带 `# 工作日志 - [YYYY-MM-DD]` 标题头的新文件再追加
+4. 同一条记录的多个文件修改合并到一行 `daily.md` 条目,不拆条
+5. `daily.md` 内容超过 8000 字符时,Agent 应在 Step 6 出示精简提醒,建议用户将旧记录蒸馏入 `project_memory.md`
+
+### 读取规则
+
+- 新会话开始时,按"记忆恢复"章节的三层加载策略读取
+- 不得预读全量记忆,必须按当前任务相关性按需加载
+- 总 token 预算控制在 1000 以内,超过时优先丢弃最旧记录
+- 读取失败时不阻塞流程,正常进入意图识别
+
+## 执行流程
+
+### 第一步:会话结束记录(Session Summary)
+
+会话结束时生成结构化summary:
+
+```markdown
+## Session Summary
+
+**日期**: [YYYY-MM-DD]
+**会话目标**: [本次会话原计划完成什么]
+
+### 已完成
+
+- [任务1]: [简要说明] → [相关文件]
+- [任务2]: [简要说明] → [相关文件]
+
+### 关键决策
+
+- **决策**: [决策内容]
+  - **上下文**: [为什么做这个决策]
+  - **影响**: [影响哪些后续工作]
+
+### 遗留问题 / Blockers
+
+- [问题1]: [描述] → [下一步行动]
+
+### 下一步计划
+
+1. [下一步任务]
+2. [下一步任务]
+
+### 规范更新
+
+- [新发现的项目规范]
+```
+
+> 模板必填项缺一不可。Session Summary 全文写入 `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/session_memory_{session-id}.jsonl`;同时向 `{PROJECT_ROOT}/.ai-memory/{YYYYMMDD}/topics.md` 写入「5 字段主题摘要」(项目 / 当前阶段 / 活跃任务 / 上次会话 / 交接状态,格式见第五步 Layer 1),供新会话快速恢复,**不写全文**。
+
+### 第二步:决策记录(Decision Record)
+
+重要技术决策时记录:
+
+```markdown
+## Decision Record: [决策名称]
+
+**日期**: [YYYY-MM-DD]
+**问题**: [需要解决什么问题]
+
+### 选项分析
+
+| 选项    | 优势 | 劣势 | 复杂度   |
+| ------- | ---- | ---- | -------- |
+| [选项A] | ...  | ...  | 低/中/高 |
+| [选项B] | ...  | ...  | 低/中/高 |
+
+### 决策
+
+**选择**: [最终选项]
+**理由**: [为什么选这个]
+**Trade-offs**: [接受了什么代价]
+
+### 影响范围
+
+- [影响的模块/文件]
+- [需要同步更新的文档]
+
+### 撤销条件
+
+[什么情况下可以重新考虑这个决策]
+```
+
+> 模板必填项缺一不可。写入位置:`{PROJECT_ROOT}/.ai-memory/project_memory.md` 的 Decision Record 章节
+
+### 第三步:规范沉淀(Convention Capture)
+
+发现项目特定规范时记录:
+
+```markdown
+## Project Convention: [规范名称]
+
+**类型**: [编码规范/命名规范/架构约束/工具配置]
+**适用范围**: [哪些模块/文件适用]
+
+### 规范内容
+
+[具体规则]
+
+### 示例
+
+**正确**:
+`[代码示例]`
+
+**错误**:
+`[代码示例]`
+
+### 发现来源
+
+[哪次会话/哪个任务中发现]
+```
+
+> 模板必填项缺一不可。
+
+### 第三步点五:术语表维护(Glossary)
+
+当对话中出现领域术语漂移(同一概念被多个名称指代、或不同概念被同一名称覆盖)时,记录到项目级术语表。
+
+**触发条件**:
+
+- 同一概念在本次会话中被 ≥ 2 种不同名称指代
+- 代码中出现的术语与需求文档中的术语不一致
+- 跨模块调用时对同一概念使用了不同命名
+- 用户纠正了 Agent 对某个术语的理解
+
+**记录格式**:
+
+```markdown
+## Glossary Entry: [术语名称]
+
+**规范名称**: [唯一推荐名称]
+**别名/变体**: [同义但不推荐的名称列表]
+**定义**: [一句话定义]
+**适用范围**: [哪些模块/文件使用此术语]
+**禁止使用**: [明确不应使用的名称]
+**来源**: [需求文档/用户口头约定/行业标准]
+```
+
+**写入位置**:追加到 `{PROJECT_ROOT}/.ai-memory/project_memory.md` 的 Glossary 章节(首次写入时创建该章节)。
+
+**使用规则**:
+
+- 后续代码生成和讨论中,Agent 必须使用术语表的规范名称
+- 发现术语表与当前使用不一致时,以术语表为准并提醒用户
+- 新会话 Step 1.1 加载 project_memory.md 时,Glossary 章节随 Section Headers 一并加载
+
+### 第三步点六:已知问题清单(Known Issues)
+
+发现新 Bug 模式、兼容性陷阱、第三方依赖风险时记录到 `{PROJECT_ROOT}/.ai-memory/project_memory.md` 的"Lessons Learned"章节:
+
+```markdown
+## Known Issue: [问题名称]
+
+**发现日期**: [YYYY-MM-DD]
+**问题类型**: [Bug模式 / 兼容性陷阱 / 依赖风险 / 性能反模式 / 安全漏洞]
+**严重度**: [致命 / 严重 / 一般 / 提示]
+
+### 现象
+
+[具体表现,含错误信息或日志片段]
+
+### 根因
+
+[根本原因分析]
+
+### 规避方案
+
+[具体的代码层面规避措施]
+
+### 相关文件
+
+- [文件路径:行号]
+
+### 状态
+
+[未修复 / 已修复 / 已规避 / 待验证]
+```
+
+> 模板必填项缺一不可。
+
+### 第四步:换机/换 IDE 交接(Handoff Protocol)
+
+当任务未完成且用户准备关闭 IDE、切换电脑、切换 IDE 或长时间暂停时,必须生成可迁移交接点。交接点写入 `{PROJECT_ROOT}/.ai-memory/handoff.md`,并提示用户随代码一起提交或同步。`handoff.md` 只记录恢复工作所需的最小上下文,不写入账号、密钥、个人聊天细节或临时 IDE 缓存路径。
+
+```markdown
+## Handoff Checkpoint
+
+**更新时间**: [YYYY-MM-DD HH:mm]
+**当前目标**: [用户原始需求的核心交付物]
+**当前阶段**: [需求/设计/实现/验证/交付]
+**完成度**: [按已验证主线数计算的百分比]
+
+### 已完成
+
+- [已完成项]: [验证证据] -> [相关文件]
+
+### 未完成
+
+- [未完成项]: [下一步动作] -> [相关文件]
+
+### 关键决策
+
+- [决策]: [理由 / 影响范围 / 撤销条件]
+
+### 恢复入口
+
+- **首读文件**: [恢复时优先读取的文件]
+- **关键命令**: [lint/test/build/启动命令]
+- **验证路径**: [继续前必须跑的验证]
+
+### 阻塞项
+
+- [阻塞原因]: 需 [角色] 提供 [具体输入]
+```
+
+**写入触发条件**:
+
+- 用户明确说“暂停/下次继续/换电脑/关闭 IDE/稍后接着做”
+- 本轮存在未完成主线任务,且已产生文件修改或关键决策
+- 任务依赖本地环境状态、运行命令、验证结果或人工确认
+- 多 IDE 协同开发时,任何会影响下一位执行者的上下文变化
+
+**同步规则**:
+
+1. 写入 `handoff.md` 前先读取现有内容,保留仍有效的未完成项,删除已验证完成项。
+2. `handoff.md` 必须随 `.ai-memory/project_memory.md` 一起纳入 Git 或共享存储同步。
+3. 新电脑恢复时,先执行 `[MEMORY-PROBE]`,再读取 `handoff.md`、`project_memory.md` 和当前任务相关文件。
+4. 恢复后若发现代码状态与 `handoff.md` 不一致,以工作区实际文件和测试结果为准,并更新交接点。
+5. 任务完成后清空或改写 `handoff.md` 为“无未完成交接项”,避免旧任务污染新会话。
+
+> 模板必填项缺一不可。写入位置:`{PROJECT_ROOT}/.ai-memory/handoff.md`,且不含敏感值。
+
+### 第五步:新会话记忆恢复(Progressive Disclosure)
+
+新会话开始时,按三层加载记忆:
+
+**Layer 1: Metadata(~50 tokens)**
+
+```
+项目: [名称]
+当前阶段: [阶段]
+活跃任务: [任务名]
+上次会话: [日期]
+交接状态: [无 / 存在 handoff.md 未完成项]
+```
+
+**Layer 2: Body(~200 tokens)**
+
+```
+## 上次Session Summary
+[最近一次的summary核心内容]
+
+## Handoff Checkpoint
+[handoff.md 中与当前任务相关的未完成项]
+
+## 活跃决策
+- [与当前任务相关的决策]
+```
+
+**Layer 3: References(按需加载,~500 tokens each)**
+
+```
+## 相关决策记录
+[与当前任务相关的完整决策记录]
+
+## 相关规范
+[与当前任务相关的项目规范]
+```
+
+**加载策略**:
+
+1. 始终加载Layer 1
+2. 如果存在 `handoff.md` 且含未完成项,优先加载与当前任务相关的 Handoff Checkpoint
+3. 如果当前任务与上次会话连续,加载Layer 2
+4. 如果涉及历史决策或规范,按需加载Layer 3
+5. 总token预算控制在1000以内,避免context rot
+
+> 记忆来源文件不存在时不阻塞流程,正常进入意图识别。
+
+## 记忆治理原则
+
+记忆按 L1(对话级)/L2(会话级)/L3(项目级)/L4(用户级)四层组织(对应上表路径)。未验证信息只留在 L1,不得沉淀为 L3;只影响本轮的临时路径不得写入 L4;L4 用户偏好与 L3 项目约束冲突时优先遵守更具体、更高风险的项目约束。
+
+记忆默认静默应用——用户长期偏好、项目固定路径、已验证技术约束、上轮已确认的下一步可静默使用;记忆与当前请求冲突、可能过期、会影响文件修改范围或交付判断时必须显式说明。
+
+敏感信息(健康/财务/身份/凭据/密钥)不得在无关上下文中引用,不得写入普通规范。开发场景例外:可记"位置与访问方式",不记"值"。
+
+记忆膨胀/冲突/透明度不足时按时间、证据强度、风险等级收敛,旧规则标注过期。失败模式与主 SKILL.md「对话流异常处理边界」对齐。
+
+### 读取截断规则
+
+从 Step 0 / Step 1.1 新会话恢复调用,用于控制单次加载量,避免上下文溢出:
+
+| 文件                | 阈值        | 读取动作                                          |
+| ------------------- | ----------- | ------------------------------------------------- |
+| `daily.md`          | ≤ 8000 字符 | 全量读取                                          |
+| `daily.md`          | > 8000 字符 | 读取末尾 120 行(最新操作),并出示精简提醒       |
+| `project_memory.md` | ≤ 8000 字符 | 全量读取                                          |
+| `project_memory.md` | > 8000 字符 | 先读目录结构 + Decision Record 章节,其余按需加载 |
+
+Step 1.1 三层策略的截断优先级:总 token > 1000 时优先丢弃 Layer 3(references),保留 Layer 1(topics)和 Layer 2(session_memory)+ handoff 检查点。
+
+### 记忆维护协议
+
+#### 30 天蒸馏周期
+
+每超过 30 天的 `{YYYYMMDD}/` 日志目录,其 `daily.md` 内容必须在下一个维护窗口中蒸馏入 `project_memory.md`:
+
+1. **蒸馏时机**:Step 6 复盘时检查,若存在 >30 天的日志目录且仍未处理,触发蒸馏
+2. **蒸馏规则**:
+   - 提取 `daily.md` 中的关键决策 → 写入 `project_memory.md` 的 Decision Record 章节
+   - 提取新发现的 Bug 模式/陷阱 → 写入 `project_memory.md` 的 Known Issues 章节
+   - 提取项目约定/偏好 → 写入 `project_memory.md` 的 Project Convention 章节
+   - 纯操作流水不蒸馏,直接丢弃
+3. **清理规则**:蒸馏完成后删除该 `{YYYYMMDD}/` 目录及其内的 `daily.md`、`topics.md` 和所有 `session_*.jsonl`
+4. **防冲突**:若 `daily.md` 中的记录与 `project_memory.md` 现有条目冲突,追加修正条目并标注来源日期,不覆盖原有记录
+
+#### daily.md 精简阈值
+
+| 阈值         | 动作                                                           |
+| ------------ | -------------------------------------------------------------- |
+| ≤ 8000 字符  | 正常追加,无需处理                                             |
+| > 8000 字符  | Step 6 出示精简提醒,询问用户是否蒸馏当日旧记录                |
+| > 12000 字符 | 强制精简:取最近 30 条记录保留,其余蒸馏入 `project_memory.md` |
+
+#### 三层记忆生命周期
+
+```
+daily.md(逐条操作日志)
+  ↓ 会话结束
+session_memory_{id}.jsonl(会话总结)+ topics.md(主题摘要)
+  ↓ >30 天
+project_memory.md(长期精选项目记忆)
+  ↓ 跨项目
+user_profile.md(用户级偏好,手动提炼)
+```
+
+## 输出格式
+
+各记忆类型的模板已在执行流程(第一/二/三/三点五/四/五步)中定义,输出时按对应模板填写。所有记忆写入 `{PROJECT_ROOT}/.ai-memory/` 或 `{USER_PROFILE}/.ai-memory/` 对应路径(见"记忆文件相对路径"表)。
+
+## 质量标准
+
+- Session Summary 会话结束时立即生成,含会话目标/已完成/关键决策/遗留问题/下一步
+- Decision Record 必须含"撤销条件",避免过早固化
+- Convention Capture 必须有正/反例,不能只有文字描述
+- Handoff Checkpoint 必须能让新电脑在不依赖旧 IDE 会话的情况下恢复当前主线
+- 记忆恢复优先加载与当前任务最相关的内容,总 token ≤ 1000
+- 记忆默认静默应用,影响交付边界时显式说明依据
+- 敏感记忆上下文隔离,开发场景可记位置不记值
+- 记忆写入按 L1-L4 分层,不得跨层污染
+
+## 失败回退机制
+
+| 步骤                      | 失败条件                     | 回退目标                                     | 最大重试 |
+| ------------------------- | ---------------------------- | -------------------------------------------- | -------- |
+| 第一步 Session Summary    | 会话信息不完整或多次中断     | 基于已有信息生成骨架,标注"信息可能不全"      | 2        |
+| 第二步 Decision Record    | 决策选项信息不足             | 记录已知选项,标注"待补充"                    | 1        |
+| 第三步 Convention Capture | 规范来源不清晰或正反例难提炼 | 仅记录规范内容,标注"示例待补充"              | 2        |
+| 第四步 Handoff            | 交接信息不完整或验证缺失     | 保留当前目标、未完成项、首读文件和阻塞项     | 1        |
+| 第五步 记忆恢复           | Layer 加载超 token 预算      | 优先丢弃 Layer 3,保留 Layer 1+2 和 handoff   | 1        |
+| 敏感记忆污染              | 敏感信息被写入普通摘要       | 立即隔离,只保留任务必要风险描述              | 0        |
+| 记忆膨胀                  | 加载大量低价值历史           | 执行记忆瘦身,仅保留有效目标/决策/约束/阻塞项 | 1        |
+| 记忆冲突                  | 多个互斥规范                 | 按时间/证据强度/风险等级裁决,旧规则标注过期  | 1        |
+
+## 关联 reference
+
+- **spec-driven-development**(Spec驱动开发)— 将spec和design决策记录到项目记忆
+- **software-project**(软件项目总控)— 将通用项目边界、架构、发布和交付记录到项目记忆
+- **website-project**(网站项目总控)— 将站点配置、部署路径、交付材料和运维清单记录到项目记忆
+- **task-decomposition-and-execution**(任务拆解与执行)— 将task summary记录到项目记忆
+- **tech-selection**(技术选型)— 选型决策自动记录到项目记忆
+- **code-generation**(代码生成)— 编码前加载项目规范,编码后记录新发现规范

+ 170 - 0
.claude/skills/dev-expert/references/refactoring.md

@@ -0,0 +1,170 @@
+# 重构建议
+
+## 输入要求
+
+1. **代码片段**(必填):需要重构的代码
+2. **重构目标**(可选):如"提高可读性"、"降低复杂度"、"便于测试"
+3. **约束条件**(可选):如"不能改变接口"、"保持向后兼容"
+
+## 执行流程
+
+### 第一步:Spec范围确认
+
+重构前确认spec范围:
+- **如有spec**:确认重构是否影响spec中的scenario,确保重构后scenario仍然通过
+- **变更范围**:明确重构影响哪些spec requirement,是否需要更新spec
+
+```
+✓ Spec范围确认:
+- 影响Scenario: [scenario列表]
+- 是否需要更新spec: [是/否]
+- 重构后验收标准: [标准]
+```
+
+### 方向语义建模门禁(图/状态机重构强制)
+
+对图遍历、环检测、状态机、依赖解析类代码的重构,**强制先建方向语义模型再动码**:
+
+- **方向三要素**:节点、边方向(有向/无向/双向)、遍历方向(down/up/both)。重构前显式写明三者,确认遍历方向与边有向性已解耦。
+- **不变量声明**:列出重构前后必须保持不变的不变量(如"环数不变""遍历方向与边方向一致""不重复覆盖平行边")。
+- **等价证明**:用具体小图(0 节点、自环、两节点互环、菱形 DAG、平行边)说明重构前后结果一致,作为验收基线。
+- 未建立方向语义模型前,不得重写遍历/迁移逻辑;否则一律按"盲目改动"回退。
+
+详见 `test-generation.md` 算法边界用例清单与 `code-review.md` 算法正确性审查清单。
+
+### 第二步:识别坏味道
+
+| 坏味道 | 特征 | 重构手法 |
+| - | - | - |
+| **过长函数** | 函数超过50行 | 提取函数 |
+| **过大类** | 类承担过多职责 | 提取类 |
+| **重复代码** | 相同/相似代码出现多次 | 提取公共函数 |
+| **过长参数列表** | 参数超过4个 | 引入参数对象 |
+| **发散式变化** | 一个类因多种原因修改 | 拆分职责 |
+| **霰弹式修改** | 修改一处需要改多个类 | 搬移函数/内联 |
+| **特性依恋** | 函数频繁访问其他类数据 | 搬移函数 |
+| **数据泥团** | 多处出现相同数据组合 | 提取对象 |
+| **Switch语句** | 大量条件分支 | 多态替代 |
+| **临时字段** | 对象某些字段只在特定场景使用 | 提取类 |
+| **过度设计** | 抽象类/接口/配置对象做简单计算 | **直接删除,回归简单** |
+| **推测性抽象** | 为"未来可能"的需求做的抽象 | **删除,需要时再创建** |
+
+### 第三步:评估重构价值(Simplicity First优先)
+
+对每个识别出的坏味道:
+- **影响范围**:影响多少代码/模块
+- **修复成本**:重构所需工作量
+- **收益**:可读性、可维护性、可测试性提升
+- **风险**:是否可能引入Bug
+
+**Karpathy优先级规则**:
+1. **简化优先**:删除过度设计和推测性抽象的收益 > 重组代码结构
+2. **删除 > 重组**:对于过度设计,直接删除回归简单,比重构成"更好的设计"优先级更高
+3. **评估标准**:重构后代码行数应该减少或持平,不应显著增加
+
+### 第四步:制定重构计划
+
+按优先级排序:
+1. 高收益低风险的优先执行
+2. 提供Before/After代码对比
+3. 说明每一步重构的具体操作
+- **简洁优先**(Karpathy原则):重构后的代码应比原来更简单
+
+### 第五步:生成重构后代码
+
+- 提供完整的重构后代码
+- 确保代码功能不变
+- 说明重构后的改进点
+- **精准修改**(Karpathy原则):每次只做一个重构,不混合多种变更
+
+### 第六步:验证
+
+- 检查重构是否引入新问题
+- 确认性能影响
+- 确保测试仍然通过
+
+### 第七步:记录到项目记忆
+
+重构完成后,将关键决策记录到项目记忆(参见 `project-memory-management.md`):
+- 记录重构决策(Decision Record 模式):为何重构、选用的重构手法
+- 记录新增的编码规范(Convention Capture 模式):如函数长度上限、类职责划分约定
+- 更新项目架构文档,反映重构后的代码结构
+
+## 输出格式
+
+```
+## 重构分析报告
+
+**分析文件**:[文件名]
+**代码行数**:[X行]
+**发现坏味道**:[X]个
+
+## 坏味道识别
+
+### 坏味道1:[名称]
+**位置**:[函数/类/行号]
+**严重程度**:高/中/低
+**原代码**:
+```[代码片段]```
+**问题说明**:[为什么这是坏味道]
+
+## 重构方案
+
+### 方案1:[重构手法名称]
+**适用坏味道**:[坏味道名称]
+**重构步骤**:
+1. [步骤1]
+2. [步骤2]
+
+**重构前**:
+```[代码]```
+
+**重构后**:
+```[代码]```
+
+**收益**:[具体改进]
+**风险**:[可能的问题]
+
+## 重构计划
+
+| 优先级 | 坏味道 | 重构手法 | 预估工作量 |
+| - | - | - | - |
+| P0 | [名称] | [手法] | [小/中/大] |
+| P1 | [名称] | [手法] | [小/中/大] |
+
+## 完整重构后代码
+
+```[完整代码]```
+```
+
+## 质量标准
+
+- 重构必须保持功能不变,不能改变原有行为
+- 每个坏味道必须引用Martin Fowler《重构》中的标准名称
+- 重构步骤必须具体到可操作(如"将第10-25行提取为calculateTotal函数")
+- 必须评估重构风险,高风险重构必须提供回滚方案
+- 重构后代码必须通过原始测试用例(如果提供)
+- 不得建议过度设计(如为简单脚本引入复杂架构)
+- **Karpathy红线**:简化是最高优先级的重构目标。删除过度设计比重组成"更好的设计"优先级更高
+- **Karpathy红线**:重构后代码行数应减少或持平,不应显著增加
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:Spec范围确认 | 无spec或spec与代码严重偏离 | 基于代码反推预期行为,标注"无spec对照" | 1 | 建议先走 Spec驱动开发 补全spec再重构 |
+| 第二步:识别坏味道 | 代码量过大,坏味道识别超限 | 按函数/类分批识别,每批输出局部清单 | 0 | 建议缩小重构范围或分模块进行 |
+| 第三步:评估重构价值(Simplicity First优先) | 坏味道收益与风险难以量化 | 默认按"高收益低风险优先",标注存疑项 | 1 | 输出收益-风险矩阵,由用户决策优先级 |
+| 第四步:制定重构计划 | 重构步骤依赖关系复杂,无法线性排序 | 按Wave分组,标注并行/串行依赖 | 2 | 输出依赖图,建议拆分为多个独立重构任务 |
+| 第五步:生成重构后代码 | 重构后代码行数显著增加(违反Karpathy红线) | 退回第三步重新评估,优先"删除>重组" | 2 | 输出两套方案(保守重构 vs 激进简化),由用户选择 |
+| 第六步:验证 | 原始测试用例缺失,无法回归 | 生成最小回归测试覆盖核心路径 | 1 | 标注"回归覆盖不足",建议先走 测试用例生成 补全基线 |
+| 第七步:记录到项目记忆 | 项目记忆系统不可用 | 输出Decision Record到本地文件 | 1 | 标注"决策未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **karpathy-coding-guidelines**(Karpathy编码规范)— 重构后可用 `karpathy-coding-guidelines` 检查是否过度设计
+- **code-review**(代码审查)— 重构前可用 `code-review` 全面评估代码问题
+- **test-generation**(测试用例生成)— 重构前可用 `test-generation` 生成基线测试保障安全
+- **code-generation**(代码生成)— 复杂重构可用 `code-generation` 生成替代实现
+- **project-memory-management**(项目记忆管理)— 记录重构决策、新增编码规范并更新架构文档
+

+ 267 - 0
.claude/skills/dev-expert/references/software-project.md

@@ -0,0 +1,267 @@
+# 软件项目总控 -- 通用软件项目从需求到交付的总控流程
+
+面向 API 服务、后台模块、插件、CLI 工具、数据处理脚本、自动化任务、中小型后端应用和非网站类功能项目。目标是在进入任务拆解前,先定义项目边界、架构方案、数据/API/集成、实现路径、验证、发布和沉淀,避免复杂需求直接落入零散代码生成。
+
+> 本 Skill 不替代 `task-decomposition-and-execution`,而是在拆解前定义通用软件项目必须覆盖的阶段和验收项;随后调用 `task-decomposition-and-execution` 将阶段转为 Wave 任务。
+
+## 输入要求
+
+1. **项目类型**(必填):API服务/后台模块/插件/CLI工具/数据脚本/自动化任务/服务端应用
+2. **业务目标**(必填):解决什么问题,交付后谁使用
+3. **运行环境**(必填):语言、框架、CMS/平台、数据库、服务器、定时任务或队列
+4. **输入输出**(必填):外部输入、数据来源、输出结果、调用方式
+5. **交付要求**(可选):上线方式、回滚要求、性能目标、安全要求、监控告警、巡检要求、验收人
+
+## 执行流程
+
+### 第一步:项目启动与边界确认
+
+- **填充项目启动模板**:按主 SKILL.md「项目启动模板」章节填写项目启动信息(含终极功能、循环轮次、改动点数),作为后续 Wave 拆解和轮次控制基准
+- 明确项目类型、业务目标、成功标准和交付边界
+- 判断是否属于网站项目;若是,转入 `website-project`
+- 明确 In scope / Out of scope,避免需求漂移
+- 确认运行入口:HTTP API、后台菜单、命令行、定时任务、队列消费、插件钩子
+
+### 第二步:需求规格与行为契约
+
+调用 `spec-driven-development` 输出需求规格,并补充:
+
+- 业务对象和状态流转
+- 输入参数、输出结果、错误码或异常类型
+- 权限、幂等、重试、审计和日志要求
+- 运行观测要求:关键指标、日志字段、告警阈值、巡检频率
+- 兼容性要求:PHP版本、CMS版本、MySQL版本、浏览器或系统环境
+
+### 第三步:架构与技术方案
+
+按项目类型调用对应子技能:
+
+- API/服务:调用 `api-design`
+- 数据库/SQL:调用 `mysql-database`
+- CMS/PHP二开:调用 `cms-development`
+- 前端/后台页面:调用 `frontend-design`
+- 技术不确定:调用 `tech-selection`
+
+必须明确:
+
+- 模块边界和文件组织
+- 数据模型、接口契约、依赖服务
+- 错误处理、日志、配置和权限
+- 可观测性方案:日志路径、指标口径、异常上报、告警接收人
+- 长任务是否需要 `Init → Step → Poll`
+- **架构权衡留痕(复杂度门槛)**:仅当本方案存在**多候选方案 / 跨模块影响 / 高影响决策**时必填——每项须附「候选方案 → 各自 trade-off → 选择理由 + 反选理由 + 撤销条件」(见 `spec-driven-development` 第四步 Design / `tech-selection` 第四步)。单一合理实现、低复杂度改动(如单文件小功能)**不强制**反选论证,避免违反"澄清过度"反模式;模糊 / 高影响决策仍须显式标注并交用户拍板。
+
+### 第三步点五:模块边界审查
+
+在进入 Wave 拆解前,对架构方案中涉及的模块边界进行合规审查,防止跨模块改动失控。
+
+**审查维度**:
+
+| 维度       | 检查项                              | 违规信号                                      |
+| ---------- | ----------------------------------- | --------------------------------------------- |
+| 依赖方向   | 模块间依赖是否单向无环              | 循环依赖 / 底层模块反向依赖上层               |
+| 接口契约   | 跨模块调用是否通过明确接口          | 跨模块直接访问内部实现 / 绕过接口调用私有方法 |
+| 数据持有   | 每个数据实体是否有唯一归属模块      | 同一张表被多个模块直接写入 / 缓存键冲突       |
+| 变更半径   | 单次改动是否需要同步修改 ≥ 3 个模块 | 一处业务规则变更需要同时改多个模块            |
+| 术语一致性 | 跨模块是否对同一概念使用同一名称    | 同一概念在不同模块有不同命名                  |
+
+> 上表为**工程边界**维度。复杂业务系统(多子域/术语多义/跨上下文协作)应先加载 `domain-driven-design` 提供**限界上下文语义边界**(同一术语在不同上下文可有不同模型),与工程边界互补——审查时先语义后工程。CRUD 为主的项目跳过语义边界,避免过度设计。
+
+**审查输出**:
+
+- 绘制模块依赖图(文本箭头图即可)
+- 违规项标注严重度(阻塞/警告/建议)和修复方向
+- 循环依赖或数据归属冲突为阻塞级,必须在进入 Wave 拆解前解决
+- 审查结果写入「架构决策」章节,随第八步沉淀到项目记忆
+
+**轻量模式**(模块数 ≤ 3 时适用):仅检查依赖方向和接口契约两项,其余跳过。
+
+### 第四步:实现计划与 Wave 拆解
+
+调用 `task-decomposition-and-execution`,按依赖拆成可验证 Wave:
+
+| Wave   | 内容                               | 验证                  |
+| ------ | ---------------------------------- | --------------------- |
+| Wave 1 | 环境、配置、入口、基础结构         | 能运行入口            |
+| Wave 2 | 数据模型、接口契约、核心服务       | 数据读写/接口返回正确 |
+| Wave 3 | 业务流程、权限、异常处理           | 主路径和异常路径通过  |
+| Wave 4 | 测试、安全、性能、兼容             | 检查清单通过          |
+| Wave 5 | 文档、部署、回滚、监控、巡检、交付 | 可交接可回滚可观测    |
+
+**轮次控制承接**(引用主 SKILL.md「项目启动模板」和「轮次与收敛」章节):
+
+- 第一步填充的项目启动模板中「循环轮次」和「每轮改动点数」字段,在本步 Wave 拆解时落地执行
+- 默认轮次 3 轮,安全最大轮次 6 轮,每轮改动点不超过 3 处
+- 每轮结束时按主 SKILL.md「收敛判定逻辑」判断是否结束(终极功能完成/默认轮次到达/安全最大轮次/连续2轮P2微调)
+- 超过安全最大轮次必须强制结束并输出未完成清单,不得无限循环
+
+### 第五步:代码实现与自检
+
+- 调用 `code-generation` 实现每个 Wave 的最小可验证切片
+- 遵循 `karpathy-coding-guidelines`:先推演、少抽象、避免模板化膨胀
+- 涉及 SQL 时遵循 `mysql-database`
+- 涉及 CMS 时遵循 `cms-development`
+- 每个改动必须给出验证方法
+
+### 第六步:审查、测试与安全验证
+
+- 调用 `code-review` 检查安全、性能、可维护性、规范符合性
+- 调用 `test-generation` 覆盖正常路径、异常路径、边界条件和回归路径
+- 检查 SQL 注入、XSS、CSRF、权限绕过、文件上传、反序列化等风险
+- 长任务必须验证 Init 创建、Step 分批、Poll 查询、Cancel 取消、失败重试和僵尸检测
+
+**证据格式(必填,对齐主流程 Step 4)**:
+
+| 验证项        | 证据类型  | 最小字段                                                                                                                                             |
+| ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 代码审查      | 命令+输出 | 审查命令 + 问题分级清单(阻塞/严重/一般/建议)                                                                                                       |
+| 单元/集成测试 | 测试报告  | 测试用例数、通过数、失败用例清单                                                                                                                     |
+| 安全验证      | 命令+输出 | 扫描命令 + 扫描结果 + 阻塞级问题清单                                                                                                                 |
+| 长任务验证    | API 响应  | Init 创建响应 + Step 进度响应 + Poll 最终状态 + Cancel 响应 + 失败重试响应 + 僵尸检测扫描结果(Poll 检测 `running` 超阈值 → 标记 `failed`/`resume`) |
+
+未声明证据类型的验证视为未完成。
+
+### 第七步:发布、回滚、监控与交付
+
+- 明确发布方式:文件覆盖、插件启用、命令部署、数据库迁移、计划任务
+- 输出上线前检查:配置、权限、依赖、缓存、日志、备份
+- 数据库变更必须有备份和回滚 SQL
+- 输出监控方案:核心可用性、错误率、慢查询、队列积压、任务失败数、磁盘/内存等运行指标
+- 输出告警方案:触发阈值、通知渠道、值班/负责人、升级路径、误报处理
+- 输出巡检方案:日/周巡检项、日志检查点、数据一致性检查、备份可恢复性检查
+- 输出交付材料:使用说明、部署说明、回滚说明、已知限制
+
+### 第八步:项目记忆沉淀
+
+调用 `project-memory-management` 记录:
+
+- 项目类型、入口、运行环境和部署方式
+- 架构决策、数据结构、接口契约和权限规则
+- 验证证据、发布记录、回滚方案、监控告警、巡检方案和已知限制
+- 后续维护注意事项
+
+## 构建与发布流水线(CI/CD)
+
+> 本节定义通用软件项目必须覆盖的构建、发布和回滚流水线要点,作为第七步的工程化落地。
+
+### 流水线阶段
+
+| 阶段     | 输入                       | 输出                           | 验证                          |
+| -------- | -------------------------- | ------------------------------ | ----------------------------- |
+| 构建     | 源码、依赖锁文件、构建配置 | 可部署产物(包/镜像/静态文件) | 构建成功且产物校验通过        |
+| 测试     | 产物 + 测试套件            | 测试报告(通过率/覆盖率)      | 单元+集成测试通过,无阻塞失败 |
+| 静态检查 | 源码                       | lint/类型检查/安全扫描报告     | 无阻塞级问题                  |
+| 部署     | 产物 + 环境配置            | 线上版本                       | 健康检查通过,旧版本可回退    |
+| 验证     | 线上环境                   | 烟雾测试结果                   | 关键路径可达,错误率低于阈值  |
+| 回滚     | 上一稳定版本               | 线上版本回退                   | 健康检查通过                  |
+
+### 强制要求
+
+- **依赖锁定**:构建必须基于 `composer.lock` / `package-lock.json` / `pnpm-lock.yaml` 等锁文件,禁止使用 `latest` / `*` / `dev-main`
+- **环境隔离**:构建、测试、生产环境配置必须分离,敏感配置通过环境变量注入,不写入代码库
+- **产物可追溯**:每次构建产物必须关联 commit、构建时间、构建号、依赖版本
+- **发布可回滚**:每次发布必须保留前一稳定版本,并能在 5 分钟内回退
+- **健康检查**:部署后必须执行烟雾测试,验证关键接口和页面可达
+- **变更窗口**:业务高峰期禁止发布,DDL/数据库迁移单独走审核流程
+
+### 失败回退
+
+| 失败场景           | 回退动作                                 |
+| ------------------ | ---------------------------------------- |
+| 构建失败           | 不进入测试;修复依赖或构建配置后重试     |
+| 测试失败           | 不进入部署;标注失败用例并修复           |
+| 部署后健康检查失败 | 立即触发自动回滚到上一稳定版本           |
+| 回滚后仍失败       | 切换到应急流程,通知负责人,保留现场日志 |
+
+## 输出格式
+
+```markdown
+## 软件项目交付方案
+
+### 1. 项目启动
+
+- 项目类型:
+- 业务目标:
+- 成功标准:
+- In scope:
+- Out of scope:
+
+### 2. 行为契约
+
+| 场景 | 输入 | 输出 | 异常/错误 | 验收标准 |
+| ---- | ---- | ---- | --------- | -------- |
+
+### 3. 技术方案
+
+| 层级 | 方案                  | 说明   |
+| ---- | --------------------- | ------ |
+| 入口 | [API/CLI/插件/任务]   | [说明] |
+| 数据 | [MySQL/文件/外部服务] | [说明] |
+| 集成 | [依赖服务]            | [说明] |
+
+### 4. Wave执行计划
+
+| Wave | 任务 | 依赖 | 验证 |
+| ---- | ---- | ---- | ---- |
+
+### 5. 验证与发布
+
+- 测试:
+- 安全:
+- 性能:
+- 发布:
+- 回滚:
+- 监控:
+- 告警:
+- 巡检:
+
+### 6. 交付材料
+
+- 使用说明:
+- 部署说明:
+- 回滚说明:
+- 运维说明:
+- 已知限制:
+```
+
+## 质量标准
+
+- 必须先定义项目类型、入口、业务目标和成功标准
+- 必须明确输入、输出、错误处理和验收标准
+- 必须说明架构边界、数据/API/集成方案
+- 涉及数据库必须调用 `mysql-database`
+- 涉及 CMS/PHP 二开必须调用 `cms-development`
+- 涉及长任务必须采用 Init → Step → Poll
+- 每个 Wave 必须有验证方法
+- 发布前必须有备份、回滚、监控告警、巡检方案和已知限制
+- 发布交付必须说明日志位置、关键指标、告警阈值、巡检频率和负责人
+- 无验证证据不得标记为完成
+
+## 失败回退机制
+
+| 步骤                           | 失败条件                         | 回退目标                       | 最大重试 | 不可恢复时升级路径             |
+| ------------------------------ | -------------------------------- | ------------------------------ | -------- | ------------------------------ |
+| 第一步:项目启动与边界确认     | 类型或边界不清                   | 输出澄清清单和默认假设         | 1        | 等待用户确认                   |
+| 第二步:需求规格与行为契约     | 输入输出不完整                   | 生成最小行为契约草案           | 1        | 标注待确认字段                 |
+| 第三步:架构与技术方案         | 技术栈不确定                     | 调用技术选型输出候选矩阵       | 1        | 由用户决策                     |
+| 第四步:实现计划与 Wave 拆解   | 依赖复杂或任务过大               | 重新按模块/入口/数据域拆分     | 2        | 输出依赖图                     |
+| 第五步:代码实现与自检         | 验证失败                         | 缩小到最小可复现切片修复       | 2        | 标记阻塞                       |
+| 第六步:审查、测试与安全验证   | 环境不可用                       | 输出手动验证清单               | 1        | 标注未实测项                   |
+| 第七步:发布、回滚、监控与交付 | 无发布权限、环境信息或监控接收人 | 输出部署包、回滚步骤和运维清单 | 1        | 由有权限人员执行并确认监控告警 |
+| 第八步:项目记忆沉淀           | 项目记忆不可用                   | 输出本地交付记录               | 1        | 标注未沉淀                     |
+
+## 关联 reference
+
+- **website-project**(网站项目总控)— 若项目属于网站、CMS站点、企业官网或营销页,转入网站总控
+- **spec-driven-development**(Spec驱动开发)— 生成需求规格、行为契约和验收标准
+- **task-decomposition-and-execution**(任务拆解与执行)— 将项目阶段拆成 Wave 并逐步验证
+- **tech-selection**(技术选型)— 技术栈、框架、数据库、队列、缓存和部署方案选型
+- **api-design**(API设计)— HTTP API、错误码、认证、分页和接口契约
+- **mysql-database**(MySQL数据库)— 表结构、索引、SQL安全、事务、慢查询和迁移回滚
+- **cms-development**(CMS二次开发)— PHP/CMS 插件、模板、后台模块和数据访问层
+- **frontend-design**(前端设计)— 后台页面、交互状态、响应式和浏览器验证
+- **code-generation**(代码生成)— 生成入口、服务、接口、脚本和业务代码
+- **code-review**(代码审查)— 审查安全、性能、可维护性和规范符合性
+- **test-generation**(测试用例生成)— 生成单元、集成、回归和边界测试
+- **doc-generation**(文档生成)— 生成 README、部署说明、使用说明、回滚说明和运维巡检文档
+- **project-memory-management**(项目记忆管理)— 沉淀项目配置、架构决策、发布记录和维护规范

+ 228 - 0
.claude/skills/dev-expert/references/spec-driven-development.md

@@ -0,0 +1,228 @@
+# Spec驱动开发 -- 需求规格对齐
+
+## 输入要求
+
+1. **需求描述**(必填):需要实现的功能或变更
+2. **项目上下文**(可选):现有系统状态、相关模块
+3. **已有spec**(可选):如果已有spec需要增量修改
+4. **约束条件**(可选):性能、安全、兼容性要求
+
+## 执行流程
+
+### 第一步:明确意图(Proposal)
+
+用3句话以内说明:
+
+- **Intent**:为什么要做这个变更
+- **Scope**:变更范围(In scope / Out of scope)
+- **Approach**:大致技术方向
+
+**示例**:
+
+```
+Intent: 用户请求添加暗黑模式以减少夜间使用时的眼疲劳
+Scope: In scope - 主题切换、系统偏好检测、localStorage持久化
+        Out of scope - 自定义颜色主题、按页面覆盖主题
+Approach: CSS自定义属性 + React Context管理状态
+```
+
+### 第二步:模糊需求深挖与边界案例
+
+遇到"合理、快速、稳定、安全、兼容、优化、友好、简单、智能、自动、尽快、可扩展"等模糊表述时,必须先转化为可验收标准,再编写 Requirements。
+
+| 深挖维度 | 需要回答的问题 | 输出要求 |
+| - | - | - |
+| 判断标准 | 什么情况下算满足?用什么指标判断? | 给出可测试阈值、规则或示例 |
+| 适用边界 | 哪些场景覆盖,哪些不覆盖? | 写入 In scope / Out of scope |
+| 正向案例 | 哪些输入/场景应当通过? | 至少 1 个 Given/When/Then |
+| 反向案例 | 哪些输入/场景应当拒绝、报错或降级? | 至少 1 个边界或失败 Scenario |
+| 常见误区 | 容易被误解成什么? | 明确不做什么,避免范围扩散 |
+| 验证证据 | 如何证明已经满足? | 测试、截图、API响应、benchmark、用户验收 |
+
+**示例**:
+
+```markdown
+模糊词:"快速"
+判断标准:列表接口 P95 响应时间 SHALL 小于 300ms,测试数据量为 10,000 条。
+正向案例:GIVEN 10,000 条数据 WHEN 请求第一页 THEN P95 < 300ms。
+反向案例:GIVEN 缺少索引 WHEN 查询复杂筛选 THEN 系统 SHALL 返回明确超时提示或降级结果。
+常见误区:不承诺所有历史数据导出都在 300ms 内完成;导出走异步任务。
+```
+
+### 第三步:编写 Requirements(ADDED/MODIFIED/REMOVED)
+
+按变更类型生成三类 Requirements,使用 RFC 2119 关键词(MUST/SHALL/SHOULD/MAY),每个 Requirement 至少一个 Scenario。
+
+#### ADDED Requirements(新增功能)
+
+```markdown
+## ADDED Requirements
+
+### Requirement: [需求名称]
+
+The system SHALL [具体行为描述].
+
+#### Scenario: [场景名称]
+
+- GIVEN [前置条件]
+- WHEN [用户操作/系统事件]
+- THEN [预期结果]
+- AND [额外预期结果]
+```
+
+#### MODIFIED Requirements(修改现有功能)
+
+```markdown
+## MODIFIED Requirements
+
+### Requirement: [需求名称]
+
+The system MUST [新行为描述].
+(Previously: [原行为描述])
+
+#### Scenario: [场景名称]
+
+- GIVEN [前置条件]
+- WHEN [用户操作/系统事件]
+- THEN [新预期结果]
+```
+
+#### REMOVED Requirements(移除功能)
+
+```markdown
+## REMOVED Requirements
+
+### Requirement: [需求名称]
+
+[移除原因说明]
+```
+
+### 第四步:编写Design(技术方案)
+
+```markdown
+## Design: [变更名称]
+
+### Technical Approach
+
+[技术实现思路,1-2段]
+
+### Architecture Decisions
+
+- Decision: [决策点]
+  - Reason: [选择理由]
+  - **Alternatives Considered**: [被否决的候选方案]
+  - **Rejected Because**: [反选理由 / 该候选的 trade-off 为何不可接受]
+  - **Trade-offs Accepted**: [本方案接受的代价与已知技术债]
+  - **Revisit When**: [什么条件下重新评估此决策]
+
+> ⚠️ 重点:存在**多候选方案 / 高影响决策**时,必须显式写出「反选理由」与「接受的代价」,仅给最终方案视为不完整——这是把架构权衡推理外显、防止盲执行的核心要求。单一合理实现的低复杂度改动可跳过反选论证,避免"澄清过度"反模式。
+
+### File Changes
+
+- `[文件路径]` (new/modified/deleted)
+```
+
+### 第五步:生成Tasks(实现清单)
+
+```markdown
+## Tasks
+
+### Wave 1(无依赖,可并行)
+
+- [ ] Task 1.1: [具体任务]
+- [ ] Task 1.2: [具体任务]
+
+### Wave 2(依赖Wave 1)
+
+- [ ] Task 2.1: [具体任务]
+```
+
+### 第六步:编码前Spec验证
+
+在开始编码前,检查:
+
+- [ ] 每个Requirement是否有至少一个Scenario
+- [ ] 每个Scenario是否可测试(有明确的Given/When/Then)
+- [ ] 成功标准是否明确("系统应该..."而非"系统可能...")
+- [ ] 变更范围是否聚焦(没有无意识扩散)
+- [ ] 模糊词是否已转化为判断标准、边界案例和验证证据
+
+**如无spec,提示**:
+
+```
+⚠️ 未检测到spec。建议先完成spec对齐再编码。
+用 `spec-driven-development` 生成spec,或提供已有spec。
+```
+
+**如有spec,作为编码依据**:
+
+```
+✓ 检测到spec。编码时将严格按以下scenario实现:
+- [Scenario列表]
+```
+
+### 第七步:记录到项目记忆
+
+Spec验证通过后,将需求规格和设计决策记录到项目记忆(参见 `project-memory-management.md`):
+- 记录 Spec 的 Intent/Scope/Approach(Decision Record 模式):作为后续编码和审查的依据
+- 记录 Design 中的架构决策和 File Changes 清单(Convention Capture 模式)
+- 若 Spec 编写中暴露需求歧义或新发现约束,更新到项目规范防止后续重蹈
+
+## 输出格式
+
+```
+## Spec: [变更名称]
+
+### Proposal
+Intent: [意图]
+Scope: [范围]
+Approach: [方向]
+
+ADDED Requirements:
+...
+
+MODIFIED Requirements:
+...
+
+REMOVED Requirements:
+...
+
+### Design
+...
+
+### Tasks
+...
+
+### Spec验证
+- [ ] Requirements可测试
+- [ ] Scenarios覆盖主路径和边界
+- [ ] 成功标准明确
+```
+
+## 质量标准
+
+- Spec是**行为契约**,不是实现计划(不写具体类名/函数名)
+- 使用RFC 2119关键词:MUST/SHALL(绝对要求)、SHOULD(建议)、MAY(可选)
+- 每个Requirement至少一个Scenario
+- Scenario使用Given/When/Then格式,可转化为自动化测试
+- 保持轻量:大多数变更使用Lite spec(简短需求+验收检查),高风险变更才用Full spec
+- 模糊需求必须先深挖为判断标准、正反边界案例和验收证据,不得直接进入编码
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:明确意图(Proposal) | 用户需求过于模糊,无法提炼Intent/Scope/Approach | 列出2-3种理解,要求用户选择 | 2 | 输出"需求澄清问卷",等待用户补充后继续 |
+| 第二步:模糊需求深挖与边界案例 | 模糊词无法转化为验收标准 | 列出 2-3 个互斥解释,请用户选择 | 2 | 输出"需求澄清问卷",等待用户补充后继续 |
+| 第三步:编写 Requirements(ADDED/MODIFIED/REMOVED) | Requirement过于宽泛,无法生成可测试Scenario | 拆分为多个子Requirement,每个对应一个Scenario | 2 | 输出Requirement骨架,标注"待细化",由用户补充 |
+| 第四步:编写Design(技术方案) | 技术方案与现有架构冲突 | 输出冲突点 + 备选方案 | 1 | 移交架构决策,输出Architecture Decision Record |
+| 第五步:生成Tasks(实现清单) | 任务依赖关系复杂,无法清晰分组为Wave | 按文件维度分组,标注"粗粒度依赖" | 1 | 输出依赖图,建议拆分为多个独立spec |
+| 第六步:编码前Spec验证 | Scenario不可测试或Given/When/Then不完整 | 退回第三步修订Scenario | 2 | 输出"spec不完整"清单,建议人工补充后再编码 |
+| 第七步:记录到项目记忆 | 项目记忆系统不可用 | 输出Spec和Design到本地文件 | 1 | 标注"规格未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **task-decomposition-and-execution**(任务拆解与执行)— spec完成后用 `task-decomposition-and-execution` 将tasks转为可执行计划
+- **code-generation**(代码生成)— 编码时用spec作为验收依据
+- **code-review**(代码审查)— 审查时检查实现是否符合spec
+- **project-memory-management**(项目记忆管理)— 将spec和design决策记录到项目记忆

+ 76 - 0
.claude/skills/dev-expert/references/style-alignment.md

@@ -0,0 +1,76 @@
+# 风格对齐(Style Alignment / 风格嗅探协议)
+
+## 适用场景
+
+二次开发(二开)、插件开发、在**已有代码库**中新增或修改模板/功能文件时**强制启用**。
+仅当目标项目确为空白、无任何可参照文件时跳过(回退到兜底约定并标注)。
+
+## 核心原则
+
+生成的代码风格必须与**目标项目已有的实际代码**一致,而不是 Agent 自身的默认风格,也不是本技能内嵌的示例风格。
+
+- 与「基于实码,不凭记忆」一致:风格也来自真实文件,不靠想象。
+- 本技能内置的风格约定(cms-development 第七步命名表、Karpathy 原则3「匹配现有风格」)仅作为「项目无参照时的兜底」,不是首选。
+- 这正是解决"二开写插件时 Agent 凭想象自由发挥模板样式"的根治手段:把"匹配现有风格"从一句声明变成**强制的取样+提取+对齐**三步动作。
+
+## 执行步骤(生成/修改前强制)
+
+### 1. 取样代表性文件
+
+按目标类型各取 1-2 个**已存在**的文件,优先取**与目标目录/模块相同**的文件(风格最贴近):
+
+- 模板/前端:一个已有的 `.php` / `.html` 混编模板
+- 后台/插件:一个已有的功能文件或后台页面
+- 表单片段:一个已有的 `fhtml` / 表单 / 列表片段
+- 配置/类:一个已有的 `config.php` / 类定义(看类、函数命名与注释风格)
+- 若项目根存在 `.editorconfig` / `phpcs.xml` / `.prettierrc` / `eslintrc`,**优先读之**作为权威基线(高于人工嗅探)
+
+### 2. 提取风格标记(逐项记录)
+
+| 标记 | 取值示例 |
+| - | - |
+| 缩进 | tab / 2 空格 / 4 空格 |
+| 开标签 | `<?php` / `<?` |
+| 括号位置 | K&R(同行列 `{`)/ Allman(换行 `{`) |
+| 数组键引号 | 加引号 `$a['x']` / 不加 `$a[x]` |
+| 引号习惯 | 单引号优先 / 双引号优先 |
+| 注释头 | 有无文件头注释块、格式样式 |
+| 模板变量命名 | `$navinfor` / `$empire` / `$public_r` 等项目约定 |
+| PHP/HTML 混编 | 缩进如何对齐、块间空行如何留 |
+| 函数/类命名 | camelCase / PascalCase / 下划线 |
+| 全等 | `===` 统一 / 混用 `==` |
+| 空行/折行 | 块间空行、链式调用折行习惯 |
+
+### 3. 产出「风格基线」块(生成前必输出)
+
+```
+## 风格基线(取自 <文件1>, <文件2>)
+- 缩进: 4 空格
+- 开标签: <?php
+- 括号: K&R
+- 数组键: 加引号
+- 引号: 单引号优先
+- 模板变量: 沿用 $navinfor/$empire 约定
+- 全等: 统一 ===
+- 混编: PHP 块与 HTML 同级缩进对齐
+```
+
+生成的每一行都必须对齐该基线。若基线的某项与**安全红线 / PHP8 兼容**冲突(如数组键不加引号违反 PHP8 兼容),以安全红线为准,并标注偏差原因。
+
+### 4. 兜底
+
+仅当目标项目确为空白、无任何可参照文件时,才回退到 cms-development 第七步内置命名约定,并明确标注「无原项目风格可参照,已用技能默认约定」。
+
+## 与现有流程衔接
+
+- `code-generation.md` 第三步「需求分析 → 加载项目规范」处强制调用本协议。
+- `cms-development.md` 第七步「代码风格与质量」改为「先嗅探原项目代表文件 → 再套用」,内置命名表降为兜底。
+- `karpathy-coding-guidelines.md` 原则3「匹配现有风格,即使你自己会不同做法」由本协议提供具体提取手段。
+
+## 失败回退
+
+| 场景 | 动作 |
+| - | - |
+| 取样文件读取失败 / 不存在 | 标注,回退兜底约定,不阻塞生成 |
+| 多个样本风格冲突 | 取与目标目录同模块者优先;仍冲突取多数派,标注少数派差异 |
+| 项目为空白无参照 | 用兜底约定并标注「无原项目风格可参照」 |

+ 232 - 0
.claude/skills/dev-expert/references/task-decomposition-and-execution.md

@@ -0,0 +1,232 @@
+# 任务拆解与执行
+
+## 输入要求
+
+1. **需求或spec**(必填):需要实现的功能描述或已有spec
+2. **现有代码**(可选):相关代码片段或文件列表
+3. **约束条件**(可选):时间、性能、依赖限制
+
+## 执行流程
+
+### 第一步:需求分析
+
+- 理解核心目标和边界
+- 不得委派理解:在拆分或调用子 Agent 前,主 Agent 必须先自行确认目标、边界、验收口径和风险点;子 Agent 只能接收明确任务,不能替主 Agent 判断用户真实意图
+- 如需求属于做网站/建站/企业官网/CMS网站/营销页/网站上线,先调用 `website-project` 确认站点规划、SEO、部署、验收和运维阶段,再进入任务拆解
+- 如需求属于 API服务/后端服务/后台模块/CLI工具/数据脚本/自动化任务/插件项目,先调用 `software-project` 确认边界、架构、数据/API、发布、回滚和交付阶段,再进入任务拆解
+- 识别隐含依赖(如"用户系统"依赖"数据库表已存在")
+- 识别风险点(复杂算法、第三方集成、性能瓶颈)
+- 识别安全触发面:外部输入、权限、数据库、文件上传、API、后台操作、长任务、第三方回调
+
+### 第二步:原子任务拆解
+
+将复杂需求拆分为2-7个原子任务(每Wave 2-5个)。每个任务必须满足:
+
+- **单一职责**:只解决一个明确问题
+- **可独立完成**:一个context window内可完成
+- **可验证**:有明确的完成标准
+- **可回滚**:失败时不影响其他任务
+- **安全可验收**:涉及输入/权限/数据/文件/API/后台操作的任务必须包含安全检查项
+- **可委派**:任务目标、输入、输出和验收标准已明确;若仍需判断用户意图或业务边界,须回第一步继续分析
+
+**任务结构(XML格式)**:
+
+```xml
+<task type="auto|human-verify|decision">
+  <name>[任务名称]</name>
+  <files>[涉及文件列表]</files>
+  <action>
+    [具体执行步骤]
+  </action>
+  <verify>
+    [验证方法:测试命令/检查清单/人工确认]
+  </verify>
+  <security>
+    [安全验证:SQL注入/XSS/CSRF/权限绕过/文件上传/反序列化/敏感信息泄露;不涉及则写"不涉及,原因:...";**禁止留空**]
+  </security>
+  <done>
+    [完成标准:什么状态算完成]
+  </done>
+</task>
+```
+
+**长任务需求专项拆解**:若需求含"批量导入/导出/生成静态页/重建索引/采集同步/清理缓存/>3 秒或 >100 条处理",不得按单端点单任务拆解,必须按 `Init → Step → Poll` 拆成依赖有序的 Wave:
+
+- **Wave 1(Init)**:校验权限/CSRF/参数,创建任务记录,计算总量 → 返回 `task_id`、`total`、`status=queued`
+- **Wave 2(Step)**:每批 20-100 条,更新进度、持久化状态、检测取消标记、幂等(用 `last_id`/游标保证每批唯一)
+- **Wave 3(Poll)**:查状态/进度/错误,检测僵尸任务(`running` 超阈值标记 `failed` 或允许 `resume`)
+- **Wave 4(Cancel)**:标记取消,正在执行的 Step 在下一批次检测取消标记后优雅退出
+- **Wave 5(前端轮询交互)**:`Init/Step/Poll/Done/Failed/Cancelled` 状态、轮询间隔 800-2000ms、重试/取消入口、进度感知
+- **Wave 6(长任务测试)**:Init/Step/Poll/失败重试/取消/超时/幂等全覆盖
+
+各 Wave 间存在依赖(Step 依赖 Init 的任务记录,Poll 依赖 Step 的进度),必须串行;权限/CSRF/文件/SQL 等安全触发面须在每 Wave 的 `<security>` 写明验证方法。
+
+### 第三步:Wave分组
+
+按依赖关系将任务分组:
+
+```
+Wave 1(并行,无依赖)
+├── Task 1.1: [任务A]
+└── Task 1.2: [任务B]
+
+Wave 2(依赖Wave 1)
+├── Task 2.1: [任务C,依赖1.1]
+└── Task 2.2: [任务D,依赖1.2]
+
+Wave 3(依赖Wave 2)
+└── Task 3.1: [任务E,依赖2.1和2.2]
+```
+
+**分组原则**:
+
+- 无依赖的任务 → 同一Wave,可并行
+- 有依赖的任务 → 后续Wave,必须串行
+- 可能文件冲突的任务 → 同一Wave内串行或合并为一个任务
+- 涉及安全触发面的Wave → 必须安排安全验证任务或在每个相关任务的 `<security>` 中给出验证方法
+
+### 第四步:上下文隔离
+
+执行单个任务时,只加载必要上下文:
+
+```
+任务上下文 = {
+  spec: [相关requirement和scenario],
+  design: [相关技术决策],
+  files: [该任务需要读写的文件],
+  decisions: [影响该任务的历史决策],
+  previous_summary: [前置Wave的任务summary]
+}
+```
+
+如平台允许子 Agent 或并行代理执行,必须使用简报式上下文,而不是传递完整对话历史:
+
+```text
+委派简报 = {
+  task: [单一任务目标],
+  background: [只保留与任务直接相关的背景],
+  files: [允许读取或修改的文件范围],
+  constraints: [技术/安全/编码/输出限制],
+  verify: [必须返回的验证证据],
+  forbidden: [不得触碰的模块、不得扩展的范围]
+}
+```
+
+**禁止加载**:
+
+- 无关模块的代码
+- 已完成的无关任务细节
+- 未决但无关的讨论
+- 完整聊天记录或未经筛选的日志
+
+> 上下文隔离同时受主 SKILL.md「二、子Agent 边界与委派协议 → 2.3」与「长任务执行可靠性(L0)」约束:长任务只加载当前 Wave / 当前原子任务所需上下文,已完成 Wave 细节仅以 `previous_summary` 传递,不回灌完整对话历史,避免上下文污染导致后续 Wave 误判。
+
+### 第五步:执行与验证闭环
+
+每个任务执行后:
+
+1. **执行**:按action完成代码修改
+2. **自测**:运行verify中的验证方法
+3. **复核结果**:对照目标完成度、格式合规性、事实准确性、上下文一致性、边界条件和安全约束逐项检查;子 Agent 输出不得未经复核直接转交给用户
+4. **记录summary**:
+
+   ```
+   ## Task Summary: [任务名]
+
+   **完成状态**: [完成/部分完成/失败]
+   **修改文件**: [文件列表]
+   **验证证据**: [命令/API响应/测试结果/人工确认]
+   **置信度**: [高/中/低,低置信度必须说明原因]
+   **关键决策**: [执行中做出的决策]
+   **偏差说明**: [与plan的差异及原因]
+   **遗留问题**: [未解决的问题]
+   ```
+
+5. **验证通过**:标记done,进入下一任务
+6. **验证失败**:修复或回滚,重新执行
+
+### 第六步:记录到项目记忆
+
+所有Wave执行完毕后,将任务执行结果和关键决策沉淀到项目记忆(参见 `project-memory-management.md`):
+
+- 汇总各任务的 summary,生成整体执行报告(Session Summary 模式)
+- 记录执行中的关键决策和偏差(Decision Record 模式):如任务拆分调整、依赖关系变更
+- 记录新发现的项目约束和规范(Convention Capture 模式):如文件组织约定、模块边界规则
+
+## 输出格式
+
+````
+## 任务拆解报告
+
+### 需求概述
+[一句话总结]
+
+### 任务列表
+
+#### Wave 1(并行)
+```xml
+<task type="auto">
+  <name>...</name>
+  <files>...</files>
+  <action>...</action>
+  <verify>...</verify>
+  <security>...</security>
+  <done>...</done>
+</task>
+```
+
+#### Wave 2(依赖Wave 1)
+
+...
+
+### 执行建议
+
+- 优先执行Wave 1,并行推进
+- Wave 2等Wave 1全部完成后再开始
+- 每个任务完成后立即验证
+
+### 风险点
+
+- [风险1及缓解措施]
+
+````
+
+## 质量标准
+
+- 每个任务必须可在单个context window完成(建议不超过200行代码变更)
+- 每个任务必须有明确的verify方法,不能是"看起来对就行"
+- Wave分组必须明确标注依赖关系
+- 涉及外部输入、权限、数据库、文件上传、API、后台操作、长任务、第三方回调的任务,必须写明安全验证方法
+- 安全验证不得只写"已检查",必须指向具体风险:SQL注入、XSS、CSRF、权限绕过、文件上传、反序列化、敏感信息泄露
+- `<security>` 节点禁止为空:涉及安全触发面必须写明验证方法,不涉及必须写明原因
+- 任务summary必须记录偏差,不能假装一切按计划进行
+- 失败任务必须说明原因,不能默默跳过
+- 子 Agent 或并行任务输出必须经过主 Agent 复核后才能计入完成度,不能把“已返回结果”等同于“已验证完成”
+- 优先"垂直切片"(端到端功能)而非"水平分层"(全部模型→全部API)
+
+## 失败回退机制
+
+| 步骤                   | 失败条件                                        | 回退目标                                | 最大重试 | 不可恢复时升级路径                          |
+| ---------------------- | ----------------------------------------------- | --------------------------------------- | -------- | ------------------------------------------- |
+| 第一步:需求分析       | 需求边界不清,隐含依赖未识别                    | 基于已知信息拆解,标注"依赖待确认"      | 1        | 输出需求澄清清单,等待用户补充              |
+| 第二步:原子任务拆解   | 任务数量超出单次context window容量              | 按模块/资源域拆分为多个Wave集合         | 1        | 建议拆分为多个独立Spec驱动开发会话          |
+| 第三步:Wave分组       | 任务间存在循环依赖                              | 识别循环并合并为单任务,或引入中间状态  | 2        | 输出依赖图,建议架构层面解耦                |
+| 第四步:上下文隔离     | 任务上下文跨Wave污染                            | 强制重置context,仅加载previous_summary | 1        | 标注"上下文隔离失败",建议人工接管          |
+| 委派执行               | 子 Agent 接收到完整对话、模糊目标或缺少验收口径 | 收回任务,重写委派简报后再执行          | 1        | 标注"委派边界失败",由主 Agent 重新分析任务 |
+| 第五步:执行与验证闭环 | 验证连续失败3次                                 | 暂停执行,输出差异分析                  | 0        | 标记任务为"阻塞",输出阻塞清单移交人工      |
+| 第六步:记录到项目记忆 | 项目记忆系统不可用                              | 输出执行报告和决策到本地文件            | 1        | 标注"执行结果未沉淀",提示用户手动保存      |
+
+## 关联 reference
+
+- **website-project**(网站项目总控)— 建站类需求拆解前先定义完整生命周期阶段和上线交付清单
+- **software-project**(软件项目总控)— 非网站类完整项目拆解前先定义边界、架构、验证、发布和交付清单
+- **spec-driven-development**(Spec驱动开发)— 拆解前用 `spec-driven-development` 确保需求已对齐
+- **api-design**(API设计)— API/服务类任务拆解前先定义接口契约、认证、错误码和分页
+- **frontend-design**(前端设计)— 页面/后台交互任务拆解前先定义组件、状态、响应式和浏览器验证
+- **cms-development**(CMS二次开发)— CMS/PHP二开任务拆解前先确认CMS访问层、插件入口和兼容性
+- **mysql-database**(MySQL数据库)— 数据库/SQL任务拆解前先定义表结构、索引、事务、迁移和回滚
+- **code-generation**(代码生成)— 每个task可用 `code-generation` 实现具体代码
+- **code-review**(代码审查)— 每个task完成后用 `code-review` 检查质量
+- **test-generation**(测试用例生成)— 每个Wave完成后补充正常、异常、边界、回归、安全、性能和长任务测试
+- **doc-generation**(文档生成)— 交付前生成README、API文档、部署说明和使用说明
+- **project-memory-management**(项目记忆管理)— 将task summary和决策记录到项目记忆

+ 191 - 0
.claude/skills/dev-expert/references/tech-selection.md

@@ -0,0 +1,191 @@
+# 技术选型 -- 智能技术选型
+
+## 输入要求
+
+1. **项目需求**(必填):项目类型、核心功能、目标用户
+2. **团队情况**(必填):团队规模、技术背景、经验水平
+3. **约束条件**(可选):预算、时间、性能要求、合规要求
+4. **已有技术栈**(可选):如果已有部分技术选型
+5. **排除选项**(可选):明确不用的技术
+
+## 执行流程
+
+### 第一步:需求分析
+
+- 识别项目类型(Web应用、移动端、数据平台、工具库等)
+- 明确核心功能和非功能需求
+- 识别关键约束(性能、安全、扩展性)
+
+### 第二步:技术分类推荐
+
+| 分类         | 考虑因素                       | 推荐维度                       |
+| ------------ | ------------------------------ | ------------------------------ |
+| **前端框架** | 交互复杂度、SEO需求、团队经验  | React/Vue/Angular/Svelte       |
+| **后端框架** | 并发量、实时性、生态成熟度     | Spring/FastAPI/Express/Django  |
+| **数据库**   | 数据模型、读写比例、一致性要求 | PostgreSQL/MySQL/MongoDB/Redis |
+| **部署运维** | 规模、预算、团队运维能力       | Docker/K8S/Serverless/VPS      |
+| **缓存消息** | 性能要求、解耦需求             | Redis/RabbitMQ/Kafka           |
+
+### 第三步:方案对比
+
+对每个候选技术:
+
+- **优势**:核心卖点、生态成熟度、社区活跃度
+- **劣势**:已知问题、学习曲线、局限性
+- **适用场景**:最佳使用场景
+- **不适用场景**:明确不适合的情况
+
+### 第四步:给出推荐
+
+- 首选方案 + 备选方案
+- 选型理由(基于需求匹配度)
+- **反选论证(必填)**:明确说明「为何不选备选方案」——逐条列出备选方案的 trade-off 为何在本项目约束下不可接受,而非仅罗列优劣
+- **接受的代价**:首选方案在本项目下的已知技术债与限制
+- 风险评估和缓解措施
+- 迁移/学习成本估算
+
+> ⚠️ 重点:技术选型必须输出「反选论证」,只给"首选+理由"视为不完整;反选论证须说明「为何不选 B」,而非仅罗列「为何选 A」。
+
+#### 选型验证(必填)
+
+推荐方案确定后,必须完成以下验证才能进入决策记录:
+
+- [ ] **可行性验证**:首选方案在目标环境是否能跑通最小原型
+- [ ] **兼容性验证**:与现有技术栈(PHP 版本/CMS/框架/数据库)兼容,无已知冲突
+- [ ] **依赖验证**:依赖包是否存在、是否维护中、是否有重大版本变更风险
+- [ ] **性能验证**:预期性能是否满足需求(如已存在 benchmark 数据则引用,否则标注"待实测")
+- [ ] **安全验证**:是否有已知 CVE、是否需要额外权限、是否引入新的攻击面
+
+如某项无法验证,必须在推荐中标注"未验证项"和风险评估,不得跳过。
+
+### 第五步:决策记录
+
+选型完成后记录决策到项目记忆:
+
+```markdown
+## Decision Record: [技术选型名称]
+
+**日期**: [YYYY-MM-DD]
+**问题**: [需要解决的技术问题]
+
+### 选项分析
+
+| 选项    | 优势 | 劣势 | 适用场景 |
+| ------- | ---- | ---- | -------- |
+| [选项A] | ...  | ...  | ...      |
+| [选项B] | ...  | ...  | ...      |
+
+### 决策
+
+**选择**: [最终选项]
+**理由**: [核心原因]
+**Trade-offs**: [接受的代价]
+
+### 影响范围
+
+- [影响的模块/项目]
+- [需要学习的成本]
+
+### 撤销条件
+
+[什么情况下重新评估]
+```
+
+**提示用户**:使用 `project-memory-management` 将此决策记录到项目记忆中。
+
+## 输出格式
+
+```
+## 技术选型报告
+
+**项目类型**:[类型]
+**团队规模**:[规模]
+**核心需求**:[需求摘要]
+
+## 推荐方案
+
+### 首选方案
+
+| 层级 | 推荐技术 | 选型理由 |
+|------|---------|---------|
+| 前端 | [技术] | [理由] |
+| 后端 | [技术] | [理由] |
+| 数据库 | [技术] | [理由] |
+| 部署 | [技术] | [理由] |
+
+### 备选方案
+
+[简要说明备选方案及适用情况]
+
+## 详细对比
+
+### [技术A] vs [技术B]
+
+| 维度 | [技术A] | [技术B] |
+|------|---------|---------|
+| 学习成本 | [高/中/低] | [高/中/低] |
+| 社区活跃度 | [高/中/低] | [高/中/低] |
+| 性能 | [优/良/一般] | [优/良/一般] |
+| 招聘难度 | [易/中/难] | [易/中/难] |
+
+## 风险评估
+
+| 风险 | 概率 | 影响 | 缓解措施 |
+|------|------|------|---------|
+| [风险1] | [高/中/低] | [高/中/低] | [措施] |
+
+## 实施建议
+
+1. [阶段1:MVP技术栈]
+2. [阶段2:扩展和优化]
+3. [阶段3:长期演进]
+```
+
+## 质量标准
+
+- 推荐必须基于具体需求,不能给通用答案
+- 必须给出至少2个候选方案的对比,不能只推荐一个
+- 风险评估必须具体
+- 必须考虑团队现有技术栈和学习成本
+- 不得推荐已停止维护或社区活跃度低的技术
+- 性能对比必须有量化依据
+
+## 失败回退机制
+
+| 步骤                 | 失败条件                              | 回退目标                               | 最大重试 | 不可恢复时升级路径                               |
+| -------------------- | ------------------------------------- | -------------------------------------- | -------- | ------------------------------------------------ |
+| 第一步:需求分析     | 项目需求过于宽泛,无法识别关键约束    | 列出常见约束清单,要求用户勾选         | 2        | 输出"需求澄清问卷",等待用户补充                 |
+| 第二步:技术分类推荐 | 团队技术背景与推荐方向严重不匹配      | 优先推荐团队熟悉的技术栈,标注学习成本 | 1        | 输出两套方案(团队熟悉 vs 技术先进),由用户权衡 |
+| 第三步:方案对比     | 候选技术缺乏量化数据                  | 标注"数据待验证",提供定性对比         | 2        | 建议POC验证后补充量化数据                        |
+| 第四步:给出推荐     | 首选与备选方案trade-off接近,难以抉择 | 输出决策矩阵,由用户最终决策           | 1        | 建议小范围POC验证后再决策                        |
+| 第五步:决策记录     | 项目记忆系统不可用                    | 输出Decision Record到本地文件          | 1        | 标注"决策未沉淀",提示用户手动保存               |
+
+### CMS PHP 版本推荐矩阵
+
+当项目涉及 PHP+MySQL CMS 时,以下矩阵为必查依据:
+
+| CMS           | 最低版本 | 推荐版本 | 不兼容提示            |
+| ------------- | -------- | -------- | --------------------- |
+| EmpireCMS 7.5 | 7.4      | 8.2      | 需 PHP 8 兼容补丁     |
+| WordPress 6.x | 7.4      | 8.2      | 部分老插件不兼容 8.3+ |
+| ThinkPHP 6    | 7.4      | 8.2      |                       |
+| ThinkPHP 8    | 8.0      | 8.2      |                       |
+| Laravel 9     | 8.0      | 8.1      |                       |
+| Laravel 10    | 8.1      | 8.2      |                       |
+| Laravel 11    | 8.2      | 8.3      |                       |
+| CodeIgniter 4 | 7.4      | 8.2      |                       |
+| DedeCMS v5.7  | 5.6      | 7.4      | 不支持 PHP 8          |
+| Hyperf        | 8.0      | 8.2      | 跟随 Swoole 版本      |
+
+**选型原则**:CMS 要求优先 → 用户指定 → 默认 8.2。详见 `cms-development.md`。
+
+## 关联 reference
+
+- **software-project**(软件项目总控)— 技术选型结果应回连 `software-project.md` 总控流程,作为架构决策记录和交付物的一部分
+- **frontend-design**(前端设计)— 前端框架/组件库/设计系统选型后可用 `frontend-design` 输出页面和组件落地规范
+- **api-design**(API设计)— 确定技术栈后可用 `api-design` 设计接口规范
+- **cms-development**(CMS二次开发)— PHP+MySQL CMS 场景参考 `cms-development.md` 的 PHP 版本推荐和 CMS 探测
+- **mysql-database**(MySQL数据库)— 选择 MySQL 或涉及 SQL/索引/事务/迁移时参考 `mysql-database.md`
+- **code-generation**(代码生成)— 确定框架后可用 `code-generation` 生成项目脚手架
+- **doc-generation**(文档生成)— 选型完成后可用 `doc-generation` 生成技术决策文档
+- **project-memory-management**(项目记忆管理)— 记录技术选型决策(Decision Record)和 CMS 版本推荐矩阵

+ 201 - 0
.claude/skills/dev-expert/references/test-generation.md

@@ -0,0 +1,201 @@
+# 测试用例生成 -- 智能测试用例生成
+
+## 输入要求
+
+1. **被测代码**(必填):需要测试的函数/类/模块
+2. **测试框架**(必填):如Jest、Pytest、JUnit、Go testing
+3. **编程语言**(必填):代码使用的语言
+4. **测试类型**(可选):单元测试/集成测试/全部,默认全部
+5. **已有测试**(可选):如果已有部分测试需要补充
+6. **专项要求**(可选):安全测试/性能测试/长任务测试/兼容性测试
+
+## 执行流程
+
+### 第一步:Spec场景覆盖检查
+
+生成测试前先检查spec中的scenario:
+- **如有spec**:确保每个scenario至少有一个对应的测试用例
+- **spec → 测试映射**:
+  ```
+  Scenario: [scenario名称] → Test: [测试用例名称]
+  ```
+- **无spec**:基于代码逻辑生成测试,但提示建议补充spec
+
+### 第二步:代码分析
+
+- 识别被测函数的输入参数和返回值
+- 识别条件分支(if/else、switch、try-catch)
+- 识别外部依赖(数据库、API、文件系统)
+- 识别边界条件(空值、空数组、最大值、最小值)
+- 识别安全触发面:外部输入、权限、SQL、HTML输出、CSRF、上传、反序列化、敏感信息
+- 识别性能触发面:循环、批量处理、分页、数据库查询、网络调用、缓存、长任务
+- 识别长任务触发面:Init 创建、Step 分批、Poll 查询、重试、取消、超时、断点续跑
+
+### 第三步:生成测试场景
+
+| 场景类型 | 说明 | 示例 |
+| - | - | - |
+| **正常路径** | 标准输入,预期成功 | 正常参数调用 |
+| **边界条件** | 临界值输入 | 空数组、最大长度、0值 |
+| **异常路径** | 错误输入,预期失败 | null、负数、类型错误 |
+| **状态相关** | 依赖对象状态的测试 | 对象初始化后/操作后 |
+| **并发相关** | 多线程/协程场景 | 同时读写、竞态条件 |
+| **安全相关** | 验证注入、越权、输出转义和令牌校验 | SQL注入payload、XSS输入、缺失CSRF token、越权访问 |
+| **性能相关** | 验证响应时间、查询次数、批量规模和资源占用 | 1000条批处理、分页查询、慢查询阈值 |
+| **长任务相关** | 验证 Init-Step-Poll 全链路和失败恢复 | Init返回task_id、Step分批推进、Poll状态变化、取消/重试 |
+
+### 第四步:编写测试代码
+
+- 使用 Arrange-Act-Assert 结构
+- 为外部依赖创建Mock/Stub
+- 使用描述性的测试名称
+- 每个测试只验证一个概念
+- 安全测试必须包含恶意输入和权限负例,断言拒绝、转义或错误码
+- 性能测试必须写明数据规模、阈值和统计口径,不能只写"性能正常"
+- 长任务测试必须覆盖 Init、Step、Poll、失败重试、取消、超时和幂等
+
+### 第五步:覆盖率分析
+
+- 语句覆盖率预估 — 按已测试语句数 / 被测函数总语句数估算;生成测试后统计 Arrange-Act-Assert 覆盖的代码行数占比
+- 分支覆盖率预估 — 按已覆盖分支数 / 被测函数总分支数估算(每个 if/else/switch/try-catch 的每个分支计1);生成测试后统计触发过的条件分支占比
+- 未覆盖路径说明
+- **安全覆盖检查**:确认输入校验、权限、输出转义、CSRF、文件上传、反序列化等触发面是否有正反例
+- **性能覆盖检查**:确认核心路径有数据规模、耗时阈值、查询次数或资源占用断言
+- **长任务覆盖检查**:确认 Init-Step-Poll、重试、取消、超时、断点续跑至少覆盖核心路径
+- **Spec覆盖检查**:确认所有scenario都有对应测试
+
+### 第六步:记录到项目记忆
+
+测试用例生成完成后,将关键决策记录到项目记忆(参见 `project-memory-management.md`):
+- 记录测试策略选择
+- 记录 Mock/Stub 约定和命名规范
+- 更新测试覆盖率基线,作为后续测试的质量参考
+
+## 实战请求示例
+
+### 示例一:接口功能测试
+
+```text
+帮我给登录接口生成测试用例。
+框架:Laravel Feature Test / PHPUnit。
+接口:POST /api/login。
+输入:email、password。
+覆盖:成功登录、密码错误、邮箱格式错误、账号禁用、频率限制。
+安全:断言不会返回 password_hash、token 只在成功时返回。
+验证:给出测试文件路径、运行命令和预期通过数。
+```
+
+优先加载:`test-generation.md`、`api-design.md`、`laravel-testing.md`。
+
+### 示例二:长任务测试
+
+```text
+帮我给批量刷新缓存任务生成测试。
+架构:Init-Step-Poll。
+覆盖:Init 返回 task_id;Step 每批 50 条;Poll 返回 percent;失败可重试;取消后不能继续执行。
+边界:空数据、超大数据、重复 Step、无权限访问。
+验证:给出单元测试和手动接口测试步骤。
+```
+
+优先加载:`test-generation.md`、`api-design.md`、`cms-development.md`。
+
+## 输出格式
+
+```
+## 测试用例报告
+
+**被测函数**:[函数名]
+**测试框架**:[框架]
+**生成测试数**:[X]个
+
+## 测试场景分析
+
+| 场景 | 输入 | 预期输出 | 类型 |
+|------|------|---------|------|
+| [场景1] | [输入] | [输出] | 正常路径 |
+| [场景2] | [输入] | [输出] | 边界条件 |
+| [场景3] | [输入] | [输出] | 异常路径 |
+| [场景4] | [输入] | [输出] | 安全/性能/长任务 |
+
+## 测试代码
+
+```[语言]
+[完整测试代码]
+```
+
+## 覆盖率预估
+
+- **语句覆盖**:[X]%
+- **分支覆盖**:[X]%
+- **安全覆盖**:[已覆盖/未覆盖,说明]
+- **性能覆盖**:[已覆盖/未覆盖,说明]
+- **长任务覆盖**:[已覆盖/不涉及,说明]
+- **未覆盖路径**:[说明]
+
+## 运行命令
+
+```bash
+[运行测试的命令]
+```
+```
+
+## 质量标准
+
+- 每个测试必须有明确的Arrange-Act-Assert结构
+- 测试名称必须描述行为和预期结果(如`shouldReturnErrorWhenInputIsNull`)
+- 边界条件必须覆盖:空值、空集合、最大值、最小值、边界值+1/-1
+- 异常测试必须验证具体的异常类型和错误消息
+- 安全测试必须覆盖适用风险:SQL注入、XSS、CSRF、权限绕过、文件上传、反序列化、敏感信息泄露
+- 性能测试必须定义数据规模、响应时间/查询次数/内存阈值,并说明环境前提
+- 长任务测试必须覆盖 Init 创建、Step 分批、Poll 查询、失败重试、取消、超时和幂等
+- 外部依赖(数据库、API、文件)必须使用Mock,不能真实调用
+- 不得生成无意义的测试(如测试getter/setter且不做断言)
+
+### 算法边界用例清单(图与递归算法)
+
+仅覆盖「空值/空集合/最大值/边界值±1」等**值域边界**不足以拦住图与递归算法缺陷。环检测这类算法三次迭代都没写对,根因是测试侧缺一张能拦住它的**结构边界**用例表。
+
+**边界应从不变量推导,而不是背诵**:凡是会让某个不变量被违反的结构,就是必须测的边界。先按 `karpathy-coding-guidelines.md` 不变量推导三步法推出「不变量命题 + 终止度量 + 守恒关系」,再取其逆否命题枚举边界——这样边界表从"背下来的表"变成"推导出来的表",面对没写进表的全新算法也能自己推出该测什么。
+
+任何图遍历 / 递归 / 状态机 / 依赖解析算法,除值域边界外必须补齐以下结构化边界(以有向图环检测为例,可推广到树、DAG、拓扑排序、可达性分析等):
+
+| 边界 | 结构 | 断言要点 |
+| - | - | - |
+| 0 节点 | 空图 | cycle=0,且不抛异常 |
+| 1 节点 | 单点无环 | cycle=0 |
+| 自环 | 单点指向自身 | cycle=1(或按业务语义) |
+| 两节点互环(down) | A→B→A | cycle=1,且遍历方向与边有向性一致 |
+| 两节点互环(both) | A⇄B(双向/无向) | cycle=1 |
+| 三环 | A→B→C→A | cycle=1,且不重复计数 |
+| 菱形 DAG | A→B→D, A→C→D | cycle=0(断言无环) |
+| 平行边 | A→B 出现两次 | 覆盖不重复、环计数不翻倍 |
+| up 方向 | 反向遍历同图 | 边方向与遍历方向一致,结果不为负 |
+
+- **递归不变量断言**:对每个递归 / DFS,必须断言不变量(invariant)在每次递归入口与出口成立,例如「已访问集合不含回边指向的节点」「递归栈深不超过节点总数」「计数单调不减」。流程跑通 ≠ 结果正确,必须有可判定的结构化断言。
+- **回边判定黑盒断言**:不要只测"能跑完",要断言具体结果(环数、环成员、是否标记、是否重复)。「流程跑通但结果错」是这类缺陷的典型漏网形态。
+- **property-based testing 提示**:对纯函数式算法(排序、环检测、拓扑排序、去重),用 property-based 框架(Python Hypothesis、JS fast-check、Java jqwik 等)生成随机结构,断言稳定性质,避免测试依赖输入顺序或样本偶然性:
+  - 单调性:给图加入一条边后,环数单调不减;
+  - 交换律:重排节点顺序 / 反转子图,结果不变;
+  - 对任意合法 DAG,拓扑排序结果与任一合法顺序一致。
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:Spec场景覆盖检查 | 无spec或spec场景与代码实现不一致 | 基于代码逻辑生成测试,标注"无spec对照" | 1 | 建议先走 Spec驱动开发 补全spec |
+| 第二步:代码分析 | 代码量过大,分支/依赖识别超限 | 按函数分批分析,每批输出局部测试 | 0 | 建议缩小测试范围或分模块生成 |
+| 第三步:生成测试场景 | 边界条件难以枚举 | 优先覆盖核心路径 + 已知边界,标注"边界待补" | 2 | 输出场景骨架,建议人工补充边界 |
+| 第四步:编写测试代码 | Mock/Stub设置复杂,外部依赖难以隔离 | 降级为集成测试,标注"未完全Mock" | 2 | 输出测试框架骨架,建议人工补全Mock |
+| 第五步:覆盖率分析 | 覆盖率无法量化(无运行环境) | 输出预估覆盖率,标注"未实际运行" | 1 | 建议用户本地运行测试后回填实际覆盖率 |
+| 第六步:记录到项目记忆 | 项目记忆系统不可用 | 输出测试策略到本地文件 | 1 | 标注"策略未沉淀",提示用户手动保存 |
+
+## 关联 reference
+
+- **frontend-design**(前端设计)— 前端页面或组件测试需参考 `frontend-design` 的交互状态、响应式断点和可访问性要求
+- **api-design**(API设计)— API 和长任务测试需参考 `api-design` 的认证、错误码、分页和 Init-Step-Poll 契约
+- **cms-development**(CMS二次开发)— CMS/PHP 测试需参考 `cms-development` 的权限、CSRF、输出转义和批处理规则
+- **mysql-database**(MySQL数据库)— SQL 和性能测试需参考 `mysql-database` 的参数绑定、索引、事务和 EXPLAIN 标准
+- **code-review**(代码审查)— 生成前可用 `code-review` 确保被测代码逻辑正确
+- **bug-diagnosis**(Bug诊断)— 修复Bug后可用 `test-generation` 生成回归测试
+- **code-generation**(代码生成)— 生成代码后可用 `test-generation` 生成配套测试
+- **project-memory-management**(项目记忆管理)— 记录测试策略、Mock/Stub 约定和测试覆盖率基线

+ 259 - 0
.claude/skills/dev-expert/references/website-project.md

@@ -0,0 +1,259 @@
+# 网站项目总控 -- 从需求到上线的建站交付流程
+
+面向企业官网、营销落地页、CMS 网站、后台管理站点、内容门户和中小型 Web 应用,提供从项目启动、需求规格、站点规划、设计实现到上线运维的完整项目级总控流程。
+
+> 本 Skill 不替代 `task-decomposition-and-execution`,而是在拆解前定义网站项目必须覆盖的阶段和验收项;随后调用 `task-decomposition-and-execution` 将阶段转为 Wave 任务。
+
+## 输入要求
+
+1. **网站类型**(必填):企业官网/营销页/CMS站点/后台管理/门户/电商/工具站
+2. **业务目标**(必填):展示品牌、获客转化、内容发布、会员服务、数据管理等
+3. **目标用户**(必填):访客、客户、运营、管理员、销售、编辑等
+4. **技术约束**(可选):CMS/PHP版本/前端框架/服务器/数据库/第三方服务
+5. **上线要求**(可选):域名、备案、SSL、上线时间、迁移范围、回滚要求
+
+## 执行流程
+
+### 第一步:项目启动与范围确认
+
+- **填充项目启动模板**:按主 SKILL.md「项目启动模板」章节填写项目启动信息(含终极功能、循环轮次、改动点数),作为后续 Wave 拆解和轮次控制基准
+- 明确网站目标、成功标准、上线时间和交付边界
+- 判断是否为 CMS 二次开发、静态站、前后端分离或后台管理系统
+- 确认角色分工:业务方、设计、前端、后端、测试、运维、验收人
+- 明确不做什么,避免范围扩散
+
+```
+✓ 项目启动信息:
+- 网站类型: [类型]
+- 业务目标: [目标]
+- 成功标准: [可验证标准]
+- In scope: [范围]
+- Out of scope: [不做内容]
+```
+
+### 第二步:需求规格与站点规划
+
+调用 `spec-driven-development` 输出行为契约,并补充网站特有规划:
+
+- 站点地图:栏目、页面类型、层级、URL 结构
+- 页面清单:首页、列表页、详情页、表单页、搜索页、登录页、后台页
+- 用户路径:访问入口、核心转化路径、异常路径
+- 内容模型:栏目、文章、产品、案例、下载、表单、配置项
+- 权限模型:游客、会员、编辑、管理员、超级管理员
+
+### 第三步:内容与 SEO 策略
+
+- 明确每类页面的 TDK:Title / Description / Keywords
+- 规划 `sitemap.xml`、`robots.txt`、canonical、面包屑
+- 图片命名、alt、尺寸比例、压缩策略
+- 结构化数据:Organization / Article / Product / Breadcrumb 等
+- 内容验收:文案、图片、栏目数据、测试数据、正式数据
+
+### 第四步:前端设计与交互方案
+
+调用 `frontend-design` 完成:
+
+- 信息架构和页面区块
+- 视觉 token、组件规范、响应式断点
+- Loading / Empty / Error / Disabled 等状态
+- 表单校验、防重复提交、二次确认
+- 浏览器验证清单
+
+### 第五步:技术方案与数据/API设计
+
+按项目类型调用对应子技能:
+
+- CMS 项目:调用 `cms-development`,确认 CMS、PHP 版本、表前缀、模板变量和插件规范
+- 接口项目:调用 `api-design`,定义认证、接口、错误码、分页和权限
+- 数据库变更:调用 `mysql-database`,明确表结构、索引、迁移、备份和回滚
+- 前后端联调:确认接口字段、空值、错误态和权限态
+- 长任务识别:批量导入/导出、生成静态页、重建索引、采集同步、图片压缩等必须采用 `Init → Step → Poll` AJAX 渐进式防卡死架构
+
+### 第六步:任务拆解与 Wave 执行
+
+调用 `task-decomposition-and-execution`,按以下阶段拆成可验证 Wave:
+
+| Wave | 内容 | 验证 |
+| - | - | - |
+| Wave 1 | 项目结构、环境、基础配置 | 能启动/能访问 |
+| Wave 2 | 数据模型、CMS字段/API接口 | 数据读写正确 |
+| Wave 3 | 页面模板/前端组件 | 页面渲染正确 |
+| Wave 4 | 交互、表单、权限、后台 | 业务路径可走通 |
+| Wave 5 | SEO、性能、安全、兼容、长任务防卡死 | 检查清单通过 |
+| Wave 6 | 部署、验收、交接 | 上线可回滚 |
+
+**轮次控制承接**(引用主 SKILL.md「项目启动模板」和「轮次与收敛」章节):
+
+- 第一步填充的项目启动模板中「循环轮次」和「每轮改动点数」字段,在本步 Wave 拆解时落地执行
+- 默认轮次 3 轮,安全最大轮次 6 轮,每轮改动点不超过 3 处
+- 每轮结束时按主 SKILL.md「收敛判定逻辑」判断是否结束(终极功能完成/默认轮次到达/安全最大轮次/连续2轮P2微调)
+- 超过安全最大轮次必须强制结束并输出未完成清单,不得无限循环
+
+### 第七步:测试、安全与兼容验证
+
+- 功能测试:核心路径、异常路径、权限路径
+- 浏览器测试:移动/平板/桌面,主流浏览器
+- 安全检查:SQL注入、XSS、CSRF、文件上传、权限绕过
+- CMS 检查:模板缓存、静态资源路径、后台字段、数据备份
+- PHP/CMS 检查:目标 PHP 版本 lint,错误日志无新增致命错误
+- 长任务检查:Init 创建任务、Step 分批处理、Poll 轮询状态,验证进度、错误、重试/取消和防重复提交
+
+### 第八步:性能优化与上线准备
+
+- 图片压缩、懒加载、WebP/AVIF 策略
+- CSS/JS 压缩、缓存策略、CDN、静态资源版本号
+- Core Web Vitals:LCP/CLS/INP 基线
+- 数据库慢查询、索引、分页、缓存
+- 上线清单:域名、SSL、备案、环境变量、定时任务、文件权限
+- 回滚方案:代码回滚、数据库备份、配置备份
+
+### 第八点五步:构建与发布流水线(CI/CD)
+
+> 网站项目同样必须覆盖构建、发布和回滚流水线要点,与 `software-project.md` 的 CI/CD 章节对齐。
+
+#### 流水线阶段
+
+| 阶段 | 输入 | 输出 | 验证 |
+| - | - | - | - |
+| 构建 | 源码、依赖锁文件、构建配置 | 静态资源包/CMS 模板包 | 构建成功且产物校验通过 |
+| 测试 | 产物 + 测试套件 | 测试报告(通过率/覆盖率) | 单元+集成+端到端测试通过 |
+| 静态检查 | 源码 | lint/类型检查/安全扫描报告 | 无阻塞级问题 |
+| 部署 | 产物 + 环境配置 | 线上版本 | 健康检查通过,旧版本可回退 |
+| 验证 | 线上环境 | 烟雾测试结果 | 关键页面可达,错误率低于阈值 |
+| 回滚 | 上一稳定版本 | 线上版本回退 | 健康检查通过 |
+
+#### 强制要求
+
+- **依赖锁定**:构建必须基于 `composer.lock` / `package-lock.json` / `pnpm-lock.yaml` 等锁文件,禁止使用 `latest` / `*` / `dev-main`
+- **环境隔离**:构建、测试、生产环境配置必须分离,敏感配置通过环境变量注入,不写入代码库
+- **产物可追溯**:每次构建产物必须关联 commit、构建时间、构建号、依赖版本
+- **发布可回滚**:每次发布必须保留前一稳定版本,并能在 5 分钟内回退
+- **健康检查**:部署后必须执行烟雾测试,验证首页、登录、表单、搜索、后台等关键路径可达
+- **变更窗口**:业务高峰期禁止发布,DDL/数据库迁移单独走审核流程
+
+#### 失败回退
+
+| 失败场景 | 回退动作 |
+| - | - |
+| 构建失败 | 不进入测试;修复依赖或构建配置后重试 |
+| 测试失败 | 不进入部署;标注失败用例并修复 |
+| 部署后健康检查失败 | 立即触发自动回滚到上一稳定版本 |
+| 回滚后仍失败 | 切换到应急流程,通知负责人,保留现场日志 |
+
+### 第九步:部署上线与验收交接
+
+- 部署到目标环境,记录发布版本和变更文件
+- 执行上线冒烟测试:访问、登录、表单、搜索、后台、核心页面
+- 接入统计和监控:PV/UV、表单转化、错误日志、访问日志
+- 交付材料:后台账号、操作手册、部署说明、回滚说明、源码路径
+- 用户验收:验收人、验收时间、验收结论、遗留问题
+
+### 第十步:运维沉淀与项目记忆
+
+调用 `project-memory-management` 记录:
+
+- 技术栈、CMS版本、PHP版本、服务器、数据库、部署路径
+- 站点地图、内容模型、模板变量、组件规范
+- 上线记录、回滚方案、账号交接、备份策略
+- 已知限制、后续优化、运维巡检清单
+
+## 输出格式
+
+```markdown
+## 网站项目交付方案
+
+### 1. 项目启动
+
+- **网站类型**: [类型]
+- **业务目标**: [目标]
+- **成功标准**: [标准]
+- **上线要求**: [时间/域名/环境]
+
+### 2. 站点地图
+
+| 页面/栏目 | URL | 内容模型 | 权限 | SEO要求 |
+| --------- | --- | --------------- | ---- | -------------- |
+| 首页 | / | 配置项/推荐内容 | 公开 | TDK/结构化数据 |
+
+### 3. 页面与功能清单
+
+| 模块 | 页面/功能 | 优先级 | 验收标准 |
+| ------ | --------- | -------- | ------------ |
+| [模块] | [功能] | P0/P1/P2 | [可验证标准] |
+
+### 4. 技术方案
+
+| 层级 | 方案 | 说明 |
+| -------- | ----------- | ------ |
+| 前端 | [技术/模板] | [说明] |
+| CMS/后端 | [技术] | [说明] |
+| 数据库 | [技术] | [说明] |
+| 部署 | [环境] | [说明] |
+
+### 5. Wave执行计划
+
+| Wave | 任务 | 依赖 | 验证 |
+| ------ | ------ | ---- | ------------- |
+| Wave 1 | [任务] | 无 | [命令/检查项] |
+
+### 6. 上线检查
+
+| 检查项 | 状态 | 证据 |
+| ------------ | ----------- | ------------------------ |
+| 域名/SSL | 通过/未通过 | [证据] |
+| SEO基础 | 通过/未通过 | [证据] |
+| 性能 | 通过/未通过 | [证据] |
+| 长任务防卡死 | 通过/未通过 | [Init/Step/Poll验证证据] |
+| 安全 | 通过/未通过 | [证据] |
+| 备份/回滚 | 通过/未通过 | [证据] |
+
+### 7. 交付材料
+
+- 后台账号:
+- 操作手册:
+- 部署说明:
+- 回滚说明:
+- 已知限制:
+```
+
+## 质量标准
+
+- 必须先定义网站类型、业务目标和成功标准,再拆任务
+- 必须包含站点地图、页面清单、内容模型和权限模型
+- 必须包含前端设计、CMS/API/数据方案、测试、安全、SEO、性能、部署、验收、运维
+- 涉及批量处理、静态生成、采集同步、导入导出等长任务时,必须包含 Init → Step → Poll 防卡死设计
+- 每个 Wave 必须有验证方法,不能只写“完成开发”
+- 上线前必须有备份和回滚方案
+- 交付必须包含后台账号、操作说明、部署说明和已知限制
+- 无验证证据不得标记为完成
+
+## 失败回退机制
+
+| 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 |
+| - | - | - | - | - |
+| 第一步:项目启动与范围确认 | 目标或范围不清 | 输出范围澄清清单 | 2 | 等待业务方确认 |
+| 第二步:需求规格与站点规划 | 页面/栏目不完整 | 生成最小站点地图草案 | 1 | 要求用户补充栏目和页面 |
+| 第三步:内容与 SEO 策略 | 文案素材缺失 | 用内容占位清单替代正式内容 | 1 | 标注素材阻塞 |
+| 第四步:前端设计与交互方案 | 无品牌规范 | 使用中性设计 token | 1 | 要求提供参考站/品牌色 |
+| 第五步:技术方案与数据/API设计 | CMS/技术栈不确定 | 进入技术选型或CMS探测 | 1 | 输出选型矩阵由用户决策 |
+| 第六步:任务拆解与 Wave 执行 | 任务依赖复杂 | 调用任务拆解按模块重组 Wave | 2 | 输出依赖图并拆分阶段 |
+| 第七步:测试、安全与兼容验证 | 环境不可用 | 输出手动验证清单 | 1 | 标注未实测项 |
+| 第八步:性能优化与上线准备 | 生产环境信息缺失 | 输出上线前置条件清单 | 1 | 等待运维/用户补充 |
+| 第九步:部署上线与验收交接 | 无上线权限 | 输出部署包和部署步骤 | 1 | 由有权限人员执行 |
+| 第十步:运维沉淀与项目记忆 | 项目记忆不可用 | 输出交付记录到本地文件 | 1 | 标注未沉淀 |
+
+## 关联 reference
+
+- **software-project**(软件项目总控)— 若项目不属于网站/CMS/营销页范畴(如 API 服务、CLI 工具、后台模块、数据脚本),转入 `software-project.md` 总控
+- **spec-driven-development**(Spec驱动开发)— 生成需求规格、Scenario 和验收标准
+- **task-decomposition-and-execution**(任务拆解与执行)— 将网站阶段拆成 Wave 并逐步验证
+- **frontend-design**(前端设计)— 输出 UI/UX、响应式、交互状态和浏览器验证方案
+- **cms-development**(CMS二次开发)— 处理 PHP+MySQL CMS、模板、插件、数据库和安全规范
+- **mysql-database**(MySQL数据库)— 处理表结构、索引、SQL安全、慢查询、迁移回滚和数据安全
+- **api-design**(API设计)— 设计接口、认证、错误码和分页规范
+- **code-generation**(代码生成)— 生成页面、组件、接口和业务代码
+- **code-review**(代码审查)— 审查安全、性能、可维护性和规范符合性
+- **test-generation**(测试用例生成)— 生成单元、集成、端到端和回归测试
+- **doc-generation**(文档生成)— 生成 README、部署说明、操作手册和交付文档
+- **project-memory-management**(项目记忆管理)— 沉淀站点配置、部署路径、运维和交付信息
+

+ 1410 - 0
.claude/skills/dev-expert/scripts/build_graph.py

@@ -0,0 +1,1410 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+build_graph.py — 项目知识图谱构建/查询工具(dev-expert)。
+
+正则抽取,结果写盘 {root}/.ai-memory/knowledge-graph/{graph.json,meta.json,symbols.json,graph.md}(不进 LLM 上下文)。
+边类型:include/use/autoload/extends/template/tpimport/import/cssimport/asset/calls(跨 PHP/JS/TS/Java/Py/CSS/HTML 多语言)。
+查询:--query <file|symbol> [--direction up|down|both] [--depth N] 裁剪子图(默认 2 跳,MAX_NODES=80)。
+--query 入口先比对 mtime+内容哈希做新鲜度检测,过期则全量重建(v1 增量=全量重抽)。
+约束:graph.md 仅人类兜底(agent 不加载);符号孤儿检测占位未实现(留口);LSP 仅记录可用性、抽取仍走正则(留口);纯 v1 正则、无 AST/tree-sitter。
+"""
+import argparse
+import hashlib
+import json
+import os
+import re
+import sys
+import time
+
+# ---------- 默认排除目录(规划 §5.6 #7) ----------
+DEFAULT_EXCLUDE_DIRS = {
+    # 依赖/构建产物
+    'vendor', 'node_modules', 'dist', 'build', '__pycache__', '.cache',
+    # 版本控制(避免抽 .git/.hg/.svn 内部文件)
+    '.git', '.hg', '.svn',
+    # 图谱产物自身(避免自引用重遍历)
+    '.ai-memory',
+    # 各 IDE / 编辑器项目级元数据目录
+    '.codebuddy',   # CodeBuddy
+    '.vscode',      # VS Code
+    '.idea',        # JetBrains
+    '.claude',      # Claude Code
+    '.cursor',      # Cursor
+    '.zed',         # Zed
+    '.fleet',       # JetBrains Fleet
+    '.sublime',     # Sublime Text
+    # 国内 AI IDE
+    '.trae',        # Trae
+    '.qoder',       # Qoder / 通义灵码
+    # 其他主流 IDE
+    '.windsurf',    # Windsurf
+    '.codeium',     # Codeium
+    '.atom',        # Atom
+    '.brackets',    # Brackets
+    '.vs',          # Visual Studio
+    '.metadata',    # Eclipse
+    '.settings',    # Eclipse
+    '.gradle',      # Gradle
+    'nbproject',    # NetBeans
+    'target',       # Maven / Eclipse 构建产物
+}
+
+# 运行时排除集(由 init_exclude_dirs 在 main 开头初始化,合并默认 + .graphignore + --exclude)
+_EXCLUDE_DIRS = set(DEFAULT_EXCLUDE_DIRS)
+
+
+def load_exclude_dirs(root, extra=None):
+    """合并默认排除 + .graphignore + CLI --exclude。
+
+    .graphignore 格式:每行一个目录名(不含路径分隔符),# 开头为注释,空行忽略。
+    """
+    dirs = set(DEFAULT_EXCLUDE_DIRS)
+    gi = os.path.join(root, '.graphignore')
+    if os.path.isfile(gi):
+        try:
+            with open(gi, encoding='utf-8', errors='ignore') as f:
+                for line in f:
+                    line = line.strip()
+                    if line and not line.startswith('#'):
+                        dirs.add(line.rstrip('/\\'))
+        except Exception:
+            pass
+    if extra:
+        dirs.update(x.strip().rstrip('/\\') for x in extra.split(',') if x.strip())
+    return dirs
+
+# ---------- 纳入图谱的源码扩展名(遍历与新鲜度检测须一致) ----------
+SRC_EXTS = ('.php', '.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx',
+            '.py', '.java', '.htm', '.html', '.css', '.scss', '.less')
+
+# ---------- 抽取正则(PHP/JS,v1 一等支持) ----------
+RE_REQ_STMT = re.compile(r"(?:require|include)(?:_once)?\s*\(?\s*(.+?)\s*\)?\s*;", re.I | re.S)
+RE_STR = re.compile(r"['\"]([^'\"]+)['\"]")
+RE_DYNAMIC_MARK = re.compile(r"\$\w+")  # 变量拼路径(如 $base . '/x')→ 抽不出
+RE_CLASS = [
+    re.compile(r"(?:class|interface|trait|enum)\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w\s,]+))?"),
+    re.compile(r"(?:public\s+|private\s+|protected\s+)?function\s+(\w+)\s*\("),
+]
+# ---------- 命名空间 / use(覆盖 composer PSR-4 框架:ThinkPHP/易优/迅睿/FastAdmin/Laravel) ----------
+RE_NAMESPACE = re.compile(r"^\s*namespace\s+([\w\\]+)\s*;", re.M)
+RE_USE = re.compile(r"^\s*use\s+([\w\\]+)(?:\s+as\s+(\w+))?\s*;", re.M)
+RE_NEW_FQN = re.compile(r"new\s+\\?([\w\\]+)\s*\(")            # new \App\X 或 new App\X
+# ---------- 非 PHP 语言模块导入(覆盖 Django/Flask/Spring/Express/NestJS/Next/React/Vue/Angular/Strapi) ----------
+RE_PY_IMPORT = re.compile(r"^\s*(?:from\s+([\w.]+)\s+import\s+\w+|import\s+([\w.]+))", re.M)
+RE_JS_REQUIRE = re.compile(r"require\s*\(\s*['\"]([^'\"]+)['\"]\s*\)")
+RE_JS_IMPORT = re.compile(r"import\s+(?:[^'\"(]*?\s+from\s+)?['\"]([^'\"]+)['\"]")
+RE_JS_DYNAMIC_IMPORT = re.compile(r"import\s*\(\s*['\"]([^'\"]+)['\"]\s*\)")
+RE_JAVA_IMPORT = re.compile(r"^\s*import\s+(?:static\s+)?([\w.]+)\s*;", re.M)
+RE_JAVA_PKG = re.compile(r"^\s*package\s+([\w.]+)\s*;", re.M)
+# ---------- ThinkPHP 显式类库导入(TP3/TP5 官方机制,thinkphp.cn/info/126) ----------
+RE_TP_IMPORT = re.compile(r"(import|vendor|Loader::import)\s*\(\s*['\"]([^'\"]+)['\"]")
+
+
+RE_TPL_INCLUDE = re.compile(
+    r"\{\s*[a-z]*:?include\b[^}]*?\bfile\s*=\s*['\"]([^'\"]+)['\"]", re.I)
+RE_TPL_REQUIRE = re.compile(r"template\s*\(\s*['\"]([^'\"]+)['\"]\s*\)")  # template('x')
+# ---------- CSS / HTML 资源依赖(前端样式与脚本依赖) ----------
+RE_CSS_IMPORT = re.compile(r"@import\s+(?:url\()?\s*['\"]?([^'\"()\s;]+)['\"]?\s*\)?\s*;", re.I)
+RE_HTML_LINK = re.compile(r"<link\b[^>]*\bhref\s*=\s*['\"]([^'\"]+)['\"]", re.I)
+RE_HTML_SCRIPT = re.compile(r"<script\b[^>]*\bsrc\s*=\s*['\"]([^'\"]+)['\"]", re.I)
+
+
+
+
+def _resolve_const_include(head, lit, rel, const_table, root):
+    """解析 require 表达式里的常量前缀拼接:CONST . 'lit' / CONST.CONST . 'lit' / 纯 CONST。
+
+    逐 token 调 resolve_const 解析常量值,路径段斜杠拼接 + 绝对/相对归一;返回相对 root 的 rel。
+    常量未定义/循环/含变量 → None(交由调用方落字面量兜底)。
+    """
+    lit = lit or ''
+    base = ''
+    ok = True
+    if re.match(r"^[A-Z_][A-Z0-9_]*$", head):
+        base = resolve_const(head, const_table, root) if head in const_table else None
+        if base is None:
+            ok = False
+    else:  # 常量链 CONST . CONST . ...
+        for tok in re.split(r"\s*\.\s*", head):
+            rb = resolve_const(tok, const_table, root) if tok in const_table else None
+            if rb is None:
+                ok = False
+                break
+            base = (base.rstrip('/') + '/' + rb.lstrip('/')) if base else rb
+    if not ok:
+        return None
+    base = os.path.normpath(base) if base else ''
+    if lit:
+        lit2 = lit.lstrip('/')
+        if base and (base.startswith('/') or re.match(r'^[A-Za-z]:', base) or os.path.isabs(base)):
+            target = os.path.normpath(base.rstrip('/') + '/' + lit2)
+        else:
+            target = os.path.normpath(os.path.join(base, lit2)) if base else os.path.normpath(lit2)
+    else:
+        target = base
+    if base and not (target.startswith('/') or re.match(r'^[A-Za-z]:', target) or os.path.isabs(target)):
+        target = os.path.normpath(os.path.join(root, target))
+    return target
+
+
+def read_text(path):
+    """读文本文件:UTF-8 BOM 自动剥 + UTF-8 解码失败回退 GBK。
+
+    解决帝国 CMS 等 GBK 编码文件的中文路径/注释抽取漏报(errors='ignore' 会丢字节致路径截断)。
+    """
+    with open(path, 'rb') as f:
+        raw = f.read()
+    if raw.startswith(b'\xef\xbb\xbf'):
+        raw = raw[3:]
+    try:
+        return raw.decode('utf-8')
+    except UnicodeDecodeError:
+        try:
+            return raw.decode('gbk')
+        except UnicodeDecodeError:
+            return raw.decode('utf-8', errors='ignore')
+
+
+def strip_php_comments(text):
+    """B2:抽取 include 前剥离 PHP 注释,避免注释/死代码被当真边。
+
+    启发式(非解析器):
+    - 块注释:字符串感知状态机剥离,跳过字符串内的 /* */(4c 增强,避免误删 $sql="/* x */")
+    - heredoc/nowdoc:跳过 heredoc 内的 # //(4b 增强,避免误剥 SQL 内 #)
+    - 行注释:// 和 #,仅当注释符前为行首空白或语句结束符才视为注释
+    已知局限:跨行字符串状态可能误判(罕见)。
+    """
+    # 1) 块注释:字符串感知状态机,跳过字符串内的 /* */
+    out_chars = []
+    in_str = False
+    quote = ''
+    i = 0
+    n = len(text)
+    while i < n:
+        ch = text[i]
+        if in_str:
+            if ch == '\\' and i + 1 < n:
+                out_chars.append(text[i:i + 2])
+                i += 2
+                continue
+            out_chars.append(ch)
+            if ch == quote:
+                in_str = False
+            i += 1
+        else:
+            if ch in ('"', "'"):
+                in_str = True
+                quote = ch
+                out_chars.append(ch)
+                i += 1
+            elif ch == '/' and i + 1 < n and text[i + 1] == '*':
+                end = text.find('*/', i + 2)
+                if end == -1:
+                    nl = text.find('\n', i)
+                    if nl == -1:
+                        nl = n
+                    out_chars.append('\n' * text[i:nl].count('\n'))
+                    i = nl
+                else:
+                    out_chars.append('\n' * text[i:end + 2].count('\n'))
+                    i = end + 2
+            else:
+                out_chars.append(ch)
+                i += 1
+    text = ''.join(out_chars)
+    # 2) 行注释:跳过 heredoc 内的 # //,仅当注释符前为行首空白或语句结束符才视为注释
+    out = []
+    in_heredoc = False
+    heredoc_tag = ''
+    for line in text.split('\n'):
+        if in_heredoc:
+            out.append(line)
+            if line.strip().rstrip(';,').rstrip() == heredoc_tag:
+                in_heredoc = False
+            continue
+        m = re.match(r'^\s*<<<?\s*([\'"]?)(\w+)\1', line)
+        if m:
+            in_heredoc = True
+            heredoc_tag = m.group(2)
+            out.append(line)
+            continue
+        # 找行注释起点:遍历字符,遇未闭合引号内的 // # 跳过
+        in_s = False
+        quote = ''
+        i = 0
+        n = len(line)
+        cut = -1
+        while i < n:
+            ch = line[i]
+            if in_s:
+                if ch == '\\':
+                    i += 2
+                    continue
+                if ch == quote:
+                    in_s = False
+            else:
+                if ch in ('"', "'"):
+                    in_s = True
+                    quote = ch
+                elif ch == '#':
+                    cut = i
+                    break
+                elif ch == '/' and i + 1 < n and line[i + 1] == '/':
+                    cut = i
+                    break
+            i += 1
+        if cut >= 0:
+            # 行注释符前需为行首空白或语句结束符,否则视为字符串/URL 内
+            head = line[:cut].rstrip()
+            if head == '' or head.endswith((';', '}', '{', ')', '?')):
+                out.append(line[:cut])
+                continue
+        out.append(line)
+    return '\n'.join(out)
+
+
+def extract_includes(text, rel, root, const_table=None):
+    """提取 include/require 目标(覆盖帝国/易优/迅睿/ThinkPHP 等 CMS)。
+
+    支持:字面量/相对路径、__DIR__/dirname 拼接、define() 常量链(递归解 RHS)、变量拼路径(动态抽不出跳过)。
+    常量无法静态解析(未定义/循环/含变量)→ 落字面量兜底,不跳过(避免误边)。
+    B2:PHP 文件抽取前先剥离注释(见 strip_php_comments)。
+    """
+    text = strip_php_comments(text)
+    const_table = const_table or {}
+    includes = []
+    for m in RE_REQ_STMT.finditer(text):
+        expr = m.group(1).strip()
+        # 1) __DIR__ / dirname(...) 拼接(保持原行为)
+        if '__DIR__' in expr or 'dirname(' in expr:
+            sm = RE_STR.search(expr)
+            if sm:
+                raw = sm.group(1)
+                target = os.path.normpath(os.path.join(os.path.dirname(rel), raw.lstrip('/')))
+                includes.append(target)
+            continue
+        # 2) define() 常量前缀拼接:CONST . 'lit' / 链 / 纯 CONST(_resolve_const_include 提取,语义 1:1 保留)。
+        # 刻意不复用 _resolve_const_rhs:其路径段拼接不加斜杠,多常量链(A.B)会生成 e/asubx.php 错误路径。
+        cm = re.match(
+            r"^((?:[A-Z_][A-Z0-9_]*)(?:\s*\.\s*[A-Z_][A-Z0-9_]*)*)"
+            r"\s*\.\s*['\"]([^'\"]*)['\"]$", expr) or \
+            re.match(r"^([A-Z_][A-Z0-9_]*)$", expr)
+        if cm:
+            target = _resolve_const_include(cm.group(1),
+                                            cm.group(2) if len(cm.groups()) >= 2 else '',
+                                            rel, const_table, root)
+            if target is not None:
+                includes.append(target)
+                continue
+            # 常量未定义或无法静态解析 → 落到下方字面量兜底(旧行为),不跳过
+        # 3) 字面量 / 相对路径(原逻辑)
+        sm = RE_STR.search(expr)
+        if not sm:
+            continue
+        raw = sm.group(1)
+        # 变量拼路径(如 $base . '/x')→ 动态,正则抽不出,跳过(规划漏抽边界)
+        if RE_DYNAMIC_MARK.search(expr):
+            continue
+        if raw.startswith('/') or re.match(r'^[A-Za-z]:', raw):
+            target = os.path.normpath(raw)
+        elif raw.startswith('./') or raw.startswith('../'):
+            target = os.path.normpath(os.path.join(os.path.dirname(rel), raw))
+        else:
+            # 无前缀 include_path 风格,回退 root
+            target = os.path.normpath(os.path.join(os.path.dirname(rel), raw))
+            if not os.path.exists(target):
+                target = os.path.normpath(os.path.join(root, raw))
+        includes.append(target)
+    return includes
+
+
+def file_hash(path):
+    """返回文件 md5;无读权限/不存在等异常返回 None(B4:避免 need_rebuild/build 因单文件不可读而整体崩溃)。"""
+    try:
+        h = hashlib.md5()
+        with open(path, 'rb') as f:
+            for chunk in iter(lambda: f.read(8192), b''):
+                h.update(chunk)
+        return h.hexdigest()
+    except Exception:
+        return None
+
+
+def _safe_mtime(path):
+    """返回文件 mtime;异常返回 None(need_rebuild 的廉价新鲜度前置过滤用)。"""
+    try:
+        return os.path.getmtime(path)
+    except Exception:
+        return None
+
+
+def detect_lsp(root):
+    """运行时 LSP 探测(仅记录可用性,v1 抽取仍走正则)。"""
+    import shutil
+    avail = {}
+    # D-2:统一用 shutil.which 探测(移除跨平台脆弱的 os.system 同步阻塞);未命中 → False(仅可用性记录)。
+    try:
+        avail['php'] = bool(shutil.which('intelephense') or shutil.which('phpactor'))
+    except Exception:
+        avail['php'] = False
+    # 其他语言默认 False(本机实测)
+    for lang in ('js', 'python', 'java', 'go'):
+        avail[lang] = False
+    return avail
+
+
+# ---------- define() 常量前缀解析(覆盖国内 CMS/框架) ----------
+RE_DEFINE = re.compile(
+    r"define\s*\(\s*['\"]([A-Za-z_][A-Za-z0-9_]*)['\"]\s*,\s*(.+?)\s*\)\s*;",
+    re.I | re.S)
+RE_CONST_DECL = re.compile(
+    r"(?:public\s+|private\s+|protected\s+|final\s+|static\s+)*"
+    r"const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([^;]+?)\s*;", re.I)
+
+
+def extract_defines(text, rel):
+    """抽取本文件 define()/const 常量 -> {NAME: (raw_rhs, defining_rel)}。"""
+    out = {}
+    for m in RE_DEFINE.finditer(text):
+        out[m.group(1)] = (m.group(2).strip(), rel)
+    for m in RE_CONST_DECL.finditer(text):
+        out[m.group(1)] = (m.group(2).strip(), rel)
+    return out
+
+
+def _resolve_segment(seg, const_table, root, seen):
+    """解析单个拼接段:字面量 / dirname() / 裸常量名。无法静态解析返回 None。"""
+    seg = seg.strip()
+    if not seg:
+        return ''
+    sm = re.match(r"^['\"]([^'\"]*)['\"]$", seg)
+    if sm:
+        return sm.group(1)
+    dm = re.match(r"^dirname\(\s*(.*?)\s*\)$", seg)
+    if dm:
+        inner = _resolve_segment(dm.group(1), const_table, root, seen)
+        if inner is None:
+            return None
+        return os.path.dirname(inner)
+    if re.match(r"^[A-Z_][A-Z0-9_]*$", seg):
+        return resolve_const(seg, const_table, root, seen)
+    return None  # 含 $ / 函数调用等无法静态解析
+
+
+def _resolve_const_rhs(raw, defining_rel, const_table, root, seen):
+    """解析常量 RHS:字面量 / . 拼接链;__DIR__/__FILE__ 用 defining_rel 替换。"""
+    raw = raw.strip()
+    sm = re.match(r"^['\"]([^'\"]*)['\"]$", raw)
+    if sm:
+        return sm.group(1)
+    work = raw
+    if '__DIR__' in work:
+        work = work.replace('__DIR__',
+                            "'" + os.path.normpath(os.path.join(root,
+                                os.path.dirname(defining_rel))).replace(os.sep, '/') + "'")
+    if '__FILE__' in work:
+        work = work.replace('__FILE__',
+                            "'" + os.path.normpath(os.path.join(root,
+                                defining_rel)).replace(os.sep, '/') + "'")
+    # 按 ` . `(带空格的拼接运算符)切分,避免误切字符串内的点(如 .php)
+    parts = re.split(r"\s+\.\s+", work)
+    res = ''
+    for p in parts:
+        if p.strip() == '':
+            continue
+        seg = _resolve_segment(p, const_table, root, seen)
+        if seg is None:
+            return None
+        res += seg
+    if res:
+        res = os.path.normpath(res)
+        # 解析结果若落在 root 内(绝对),转回相对路径,保持图谱可移植
+        if os.path.isabs(res) and res.startswith(os.path.abspath(root)):
+            res = os.path.relpath(res, os.path.abspath(root)).replace(os.sep, '/')
+            if res == '.':
+                res = ''  # 解析结果恰为项目根 → 空串,避免拼接出 '.extend' 这类错误路径
+        return res
+    return None
+
+
+def resolve_const(name, const_table, root, seen=None):
+    """递归解析一个 define 常量的值为真实路径片段;循环/未定义/含变量返回 None。"""
+    if seen is None:
+        seen = set()
+    if name in seen:
+        return None
+    entry = const_table.get(name)
+    if not entry:
+        return None
+    raw, drel = entry
+    seen.add(name)
+    return _resolve_const_rhs(raw, drel, const_table, root, seen)
+
+
+def parse_composer(root):
+    """解析 composer.json 的 autoload(psr-4 / psr-0 / files / classmap)。
+
+    返回 {psr4: {prefix: dir}, psr0: {prefix: dir}, files: [rel...], classmap: [dir...]}。
+    国内 ThinkPHP/易优/迅睿/FastAdmin/Laravel 等框架靠此把命名空间映射到文件。
+    """
+    out = {'psr4': {}, 'psr0': {}, 'files': [], 'classmap': []}
+    cj = os.path.join(root, 'composer.json')
+    if not os.path.isfile(cj):
+        return out
+    try:
+        data = json.load(open(cj, encoding='utf-8', errors='ignore'))
+    except Exception:
+        return out
+    al = data.get('autoload', {})
+    for kind, key in (('psr4', 'psr-4'), ('psr0', 'psr-0')):
+        for pref, d in (al.get(key) or {}).items():
+            pref = pref.rstrip('/').rstrip('\\')  # A-1:剥尾部反斜杠,否则 fqn_to_file 的 pref+'\\' 拼接多一反斜杠致 startswith 永 False
+            if isinstance(d, list):
+                for x in d:
+                    out[kind][pref] = x.rstrip('/').rstrip('\\')
+            else:
+                out[kind][pref] = d.rstrip('/').rstrip('\\')
+    for f in (al.get('files') or []):
+        out['files'].append(f)
+    for cm in (al.get('classmap') or []):
+        out['classmap'].append(cm)
+    return out
+
+
+def fqn_to_file(fqn, composer, root):
+    """按 PSR-4 / PSR-0 把完全限定类名映射到文件路径;找不到返回 None。"""
+    fqn = fqn.lstrip('\\')
+    parts = fqn.split('\\')
+    # PSR-4:最长前缀匹配
+    for pref in sorted(composer['psr4'], key=len, reverse=True):
+        if fqn == pref or fqn.startswith(pref + '\\'):
+            rest = fqn[len(pref):].lstrip('\\').split('\\')
+            d = composer['psr4'][pref]
+            rel = os.path.normpath(os.path.join(d, *rest)) + '.php'
+            if os.path.isfile(os.path.join(root, rel)):
+                return rel.replace(os.sep, '/')
+            return rel.replace(os.sep, '/')  # 即便文件暂缺也返回预期路径(标悬空)
+    # PSR-0:每段映射(\ 和 _ 都转目录分隔符,PSR-0 规范)
+    for pref in sorted(composer['psr0'], key=len, reverse=True):
+        if fqn == pref or fqn.startswith(pref + '\\'):
+            rest = fqn[len(pref):].lstrip('\\').replace('\\', '/').replace('_', '/')
+            rest_parts = [p for p in rest.split('/') if p]
+            d = composer['psr0'][pref]
+            rel = os.path.normpath(os.path.join(d, *rest_parts)) + '.php'
+            if os.path.isfile(os.path.join(root, rel)):
+                return rel.replace(os.sep, '/')
+            return rel.replace(os.sep, '/')
+    return None
+
+
+def resolve_module(spec, lang, rel, root):
+    """按语言把 import 说明符解析为项目内文件 rel(或 None):Python/JS(ts)/Java。
+    相对路径优先相对源文件;裸说明符回退 node_modules / 包根启发式。
+    """
+    spec = spec.strip()
+    if not spec:
+        return None
+    # rel 在 Windows 下是 '/' 分隔,os.path.dirname 不认 '/',须转成本地分隔符
+    rel_native = rel.replace('/', os.sep)
+    src_dir = os.path.dirname(rel_native)
+    src_full = os.path.join(root, src_dir) if src_dir else root
+    if lang == 'py':
+        parts = spec.split('.')
+        if parts and parts[0] == '':
+            parts = parts[1:]  # 去前导点(.models)
+        cands = []
+        for base in (src_full, root):
+            cands.append(os.path.join(base, *parts) + '.py')
+            cands.append(os.path.join(base, *parts, '__init__.py'))
+        for c in cands:
+            if os.path.isfile(c):
+                return os.path.relpath(c, root).replace(os.sep, '/')
+        return None
+    if lang in ('js', 'ts'):
+        if spec.startswith('./') or spec.startswith('../'):
+            base = os.path.normpath(os.path.join(src_full, spec))
+            # 若说明符自身已带扩展名(./styles.css),优先按原样试
+            if os.path.splitext(spec)[1]:
+                if os.path.isfile(base):
+                    return os.path.relpath(base, root).replace(os.sep, '/')
+            for ext in ('.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs', '.json', '.css', '.scss', '.less'):
+                c = base + ext
+                if os.path.isfile(c):
+                    return os.path.relpath(c, root).replace(os.sep, '/')
+            for idx in ('index.js', 'index.ts', 'index.jsx', 'index.tsx'):
+                c = os.path.join(base, idx)
+                if os.path.isfile(c):
+                    return os.path.relpath(c, root).replace(os.sep, '/')
+            return None
+        # 裸说明符:node_modules 启发式
+        nm_dirs = [os.path.join(root, 'node_modules')]
+        if src_dir:
+            nm_dirs.append(os.path.join(root, src_dir, 'node_modules'))
+        for nm in (spec, spec.lstrip('@')):
+            for top in nm_dirs:
+                c = os.path.join(top, nm)
+                for entry in ('index.js', 'index.ts', 'index.jsx', 'index.tsx', 'src/index.js', 'src/index.ts'):
+                    if os.path.isfile(os.path.join(c, entry)):
+                        return os.path.relpath(os.path.join(c, entry), root).replace(os.sep, '/')
+        return None
+    if lang == 'java':
+        parts = spec.split('.')
+        rest = parts[:-1] if spec.endswith('.*') else parts
+        relp = os.path.normpath(os.path.join(*rest)) + '.java'
+        c = os.path.join(root, relp)
+        if os.path.isfile(c):
+            return os.path.relpath(c, root).replace(os.sep, '/')
+        # 包路径相对源码根(com/x/Y.java 可能位于 root 下任意 java/ 子目录),递归搜索
+        for dp, _, fns in os.walk(root):
+            if any(x in dp for x in _EXCLUDE_DIRS):
+                continue
+            cand = os.path.join(dp, relp)
+            if os.path.isfile(cand):
+                return os.path.relpath(cand, root).replace(os.sep, '/')
+        return None
+    return None
+
+
+def resolve_tp_import(spec, root, const_table):
+    """解析 ThinkPHP import()/vendor()/Loader::import()(官方规则 thinkphp.cn/info/126)。
+
+    点号转目录;基库前缀映射(依赖项目定义的常量,经 resolve_const 解析):
+      Think.*   -> THINK_PATH/Lib/*           (去 Think 前缀,后缀 .class.php)
+      ORG.*     -> EXTEND_PATH/Library/ORG/*  (保留 ORG 段,后缀 .class.php)
+      Com.*     -> EXTEND_PATH/Library/Com/*  (保留 Com 段,后缀 .class.php)
+      Vendor.*  -> VENDOR_PATH/*              (去 Vendor 前缀;vendor() 调用已补 Vendor.)
+      @.*       -> APP_PATH/*                 (去 @ 前缀,后缀 .class.php)
+      其它(项目类库,如 Common.Tool/MyApp.Action.User) -> APP_PATH/* 完整点路径(.class.php 优先,缺失回退 .php)
+    别名导入(单段无点,如 import('rbac')) 需 alias.php 映射,静态难穷举,留口 None。
+    """
+    spec = spec.strip()
+    if not spec or '.' not in spec:
+        return None  # 别名/多参仅首参带点形式留口
+    parts = spec.split('.')
+    head = parts[0]
+    if spec.startswith('@'):
+        base = resolve_const('APP_PATH', const_table, root) or 'application'
+        parts = parts[1:]                      # 去 @ 别名段
+        suffix = '.class.php'
+    elif head == 'Think':
+        tp = resolve_const('THINK_PATH', const_table, root) or \
+            resolve_const('LIB_PATH', const_table, root) or 'ThinkPHP'
+        base = (tp.rstrip('/') + '/Lib') if tp else 'ThinkPHP/Lib'
+        parts = parts[1:]                      # 去 Think 前缀
+        suffix = '.class.php'
+    elif head in ('ORG', 'Com'):
+        ep = resolve_const('EXTEND_PATH', const_table, root) or 'Extend/Library'
+        base = (ep.rstrip('/') + '/Library') if ep else 'Extend/Library'
+        # 保留 ORG/Com 段(EXTEND_PATH/Library/ORG/...);用首段精确匹配避免 Common 误命中 Com
+        suffix = '.class.php'
+    elif head == 'Vendor':
+        vp = resolve_const('VENDOR_PATH', const_table, root) or 'Vendor'
+        base = vp.rstrip('/') if vp else 'Vendor'
+        parts = parts[1:]                      # 去 Vendor 前缀(base 已是 VENDOR_PATH)
+        suffix = '.class.php'
+    else:  # 项目/应用类库:完整点路径映射目录(Common.Tool 不会误命中 Com 前缀)
+        base = resolve_const('APP_PATH', const_table, root) or 'application'
+        suffix = '.class.php'
+    if not base or not parts:
+        return None
+    relp = os.path.normpath(os.path.join(base, *parts)) + suffix
+    c = os.path.join(root, relp)
+    if os.path.isfile(c):
+        return os.path.relpath(c, root).replace(os.sep, '/')
+    # 项目类库非标准后缀回退 .php(import('X', APP_PATH, '.php') 形态)
+    if suffix == '.class.php':
+        c2 = os.path.join(root, os.path.normpath(os.path.join(base, *parts)) + '.php')
+        if os.path.isfile(c2):
+            return os.path.relpath(c2, root).replace(os.sep, '/')
+    return None
+
+
+def extract_file(path, root, const_table=None):
+    """抽取单文件:返回 {includes, classes, calls, namespace, uses, tpl_includes}。"""
+    text = ''
+    try:
+        text = read_text(path)
+    except Exception:
+        return {'includes': [], 'classes': [], 'calls': [], 'namespace': None,
+                'uses': [], 'tpl_includes': []}
+    rel = os.path.relpath(path, root).replace(os.sep, '/')
+    # include/require 是 PHP 专属语法;其余语言走各自模块导入机制,禁止用 PHP 正则误抽(否则 JS `src:` 等成噪声边)。
+    PHP_EXTS = ('.php', '.inc', '.phtml', '.php3', '.php4', '.php5')
+    # B-?:符号抽取须在剥离 PHP 注释后进行(避免注释/死代码里的 `// class X` 被当真实符号生成假边)。
+    # extract_includes 内部已自剥离;此处对非 include 的符号层统一前置剥离(仅 PHP 文件,非 PHP 仍用原文)。
+    code = strip_php_comments(text) if rel.endswith(PHP_EXTS) else text
+    includes = extract_includes(text, rel, root, const_table) if rel.endswith(PHP_EXTS) else []
+    # PHP 专属符号抽取(namespace/use/class/function/calls/new/::/FQN/tpimport)仅对 PHP 运行;
+    # 非 PHP 文件这些字段置空,禁止 .py/.js/.ts/.java 被 PHP 正则误抽污染符号表(ERR-006 同族)。
+    if rel.endswith(PHP_EXTS):
+        ns_m = RE_NAMESPACE.search(code)
+        namespace = ns_m.group(1) if ns_m else None
+        uses = [{'fqn': m.group(1), 'alias': m.group(2)} for m in RE_USE.finditer(code)]
+        classes = []
+        for m in RE_CLASS[0].finditer(code):
+            cls = m.group(1)
+            ext = m.group(2)
+            impl = m.group(3)
+            classes.append({'name': cls, 'line': code[:m.start()].count('\n') + 1,
+                            'extends': ext, 'implements': impl})
+        funcs = []
+        for m in RE_CLASS[1].finditer(code):
+            funcs.append(m.group(1))
+        # calls:new X() / X::const(跨文件才记,规划 §4 敏感边约束)
+        # 注:$obj->method() 抽不出类名(变量非类型,静态分析边界),属动态调用漏抽,
+        #     由 G2' grep 复核兜底;保留 new/:: 两类可静态解析类名的调用。
+        calls = []
+        for m in re.finditer(r"new\s+(\w+)\s*\(", code):
+            calls.append(m.group(1))
+        for m in re.finditer(r"(\w+)::\w+", code):
+            calls.append(m.group(1))
+        # new \Ns\Class(框架类实例化,composer 自动加载)
+        new_fqns = [m.group(1) for m in RE_NEW_FQN.finditer(code)]
+        # ThinkPHP 显式类库导入 import()/vendor()/Loader::import()(TP3/TP5 官方机制)
+        tp_imports = [(m.group(1), m.group(2)) for m in RE_TP_IMPORT.finditer(code)]
+    else:
+        namespace = None
+        uses = []
+        classes = []
+        funcs = []
+        calls = []
+        new_fqns = []
+        tp_imports = []
+    # 模板 include 标签(帝国/Dede/Discuz/易优 模板依赖;CMS 模板标签非 PHP 语法,保留原扫描 text)
+    tpl_includes = []
+    for m in RE_TPL_INCLUDE.finditer(text):
+        tpl_includes.append(m.group(1))
+    for m in RE_TPL_REQUIRE.finditer(text):
+        tpl_includes.append(m.group(1))
+    # 非 PHP 语言模块导入(Django/Flask/Spring/React/Vue/...)
+    lang = None
+    if rel.endswith('.py'):
+        lang = 'py'
+    elif rel.endswith(('.js', '.jsx', '.mjs', '.cjs')):
+        lang = 'js'
+    elif rel.endswith(('.ts', '.tsx')):
+        lang = 'ts'
+    elif rel.endswith('.java'):
+        lang = 'java'
+    module_imports = []
+    if lang == 'py':
+        for m in RE_PY_IMPORT.finditer(text):
+            module_imports.append(m.group(1) or m.group(2))
+    elif lang in ('js', 'ts'):
+        for m in RE_JS_REQUIRE.finditer(text):
+            module_imports.append(m.group(1))
+        for m in RE_JS_IMPORT.finditer(text):
+            module_imports.append(m.group(1))
+        for m in RE_JS_DYNAMIC_IMPORT.finditer(text):
+            module_imports.append(m.group(1))
+    elif lang == 'java':
+        java_pkg = RE_JAVA_PKG.search(text)
+        for m in RE_JAVA_IMPORT.finditer(text):
+            module_imports.append(m.group(1))
+    # CSS @import 依赖(指向其它样式文件,真实样式级联依赖)
+    css_imports = []
+    if rel.endswith(('.css', '.scss', '.less')):
+        for m in RE_CSS_IMPORT.finditer(text):
+            css_imports.append(m.group(1))
+    # HTML <link href> / <script src> 资源依赖(样式表与脚本,排除图片等噪声)
+    # .php 亦输出 HTML(CMS 模板普遍在 .php 中直接写 <link>/<script>)
+    html_assets = []
+    if rel.endswith(('.php', '.htm', '.html')):
+        for m in RE_HTML_LINK.finditer(text):
+            if m.group(1).endswith(('.css', '.scss', '.less', '.js', '.jsx',
+                                     '.ts', '.tsx', '.mjs', '.cjs')):
+                html_assets.append(m.group(1))
+        for m in RE_HTML_SCRIPT.finditer(text):
+            if m.group(1).endswith(('.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs')):
+                html_assets.append(m.group(1))
+    return {'rel': rel, 'includes': includes, 'classes': classes,
+            'funcs': funcs, 'calls': calls, 'namespace': namespace,
+            'uses': uses, 'tpl_includes': tpl_includes, 'new_fqns': new_fqns,
+            'lang': lang, 'module_imports': module_imports, 'tp_imports': tp_imports,
+            'css_imports': css_imports, 'html_assets': html_assets}
+
+
+def _acquire_lock(root, max_wait=10, stale_timeout=60):
+    """获取构建锁,防多进程并发构建。等待 max_wait 秒,超 stale_timeout 的锁视为僵尸强制接管。"""
+    lock_path = os.path.join(root, '.ai-memory', 'knowledge-graph', '.lock')
+    for _ in range(max_wait):
+        if os.path.isfile(lock_path):
+            try:
+                age = time.time() - os.path.getmtime(lock_path)
+                if age > stale_timeout:
+                    break  # 僵尸锁,强制接管
+            except Exception:
+                break
+            time.sleep(1)
+        else:
+            break
+    try:
+        os.makedirs(os.path.dirname(lock_path), exist_ok=True)
+        with open(lock_path, 'w') as f:
+            f.write(str(os.getpid()))
+        return True
+    except Exception:
+        return False
+
+
+def _release_lock(root):
+    """释放构建锁。"""
+    lock_path = os.path.join(root, '.ai-memory', 'knowledge-graph', '.lock')
+    try:
+        if os.path.isfile(lock_path):
+            os.remove(lock_path)
+    except Exception:
+        pass
+
+
+def build(root):
+    """全量构建。返回 (graph, meta, symbols)。"""
+    out_dir = os.path.join(root, '.ai-memory', 'knowledge-graph')
+    _acquire_lock(root)  # 并发锁:防多进程同时构建(超 60s 僵尸锁强制接管)
+    os.makedirs(out_dir, exist_ok=True)
+    files = []
+    for dirpath, dirnames, filenames in os.walk(root):
+        dirnames[:] = [d for d in dirnames if d not in _EXCLUDE_DIRS
+                       and not d.startswith('.')]
+        for fn in filenames:
+            if fn.endswith(SRC_EXTS):
+                files.append(os.path.join(dirpath, fn))
+    file_list = [os.path.relpath(p, root).replace(os.sep, '/') for p in files]
+    lsp = detect_lsp(root)
+    # 常量预扫描(支持 define()/const 前缀拼路径的国内 CMS/框架)
+    CONST_TABLE = {}
+    for f, p in sorted(zip(file_list, files)):  # 常量预扫描按 rel 排序,避免同常量多定义时解析随遍历顺序漂移(B-1)
+        try:
+            txt = read_text(p)
+        except Exception:
+            continue
+        for nm, val in extract_defines(txt, f).items():
+            CONST_TABLE[nm] = val  # 后定义覆盖
+    extracted = {f: extract_file(p, root, CONST_TABLE) for f, p in zip(file_list, files)}
+    COMPOSER = parse_composer(root)
+
+    nodes = {}      # rel -> {type, classes, funcs}
+    edges = []      # {from, to, kind}
+    edge_keys = set()  # 边去重键集合(B-4):(from, to, kind) 精确去重,避免重复 require 产生重复边
+    symbols = {}    # name -> {file, line}
+    fqn_symbols = {}  # FQN -> file(命名空间类,覆盖 composer 框架)
+    dangling = []
+    orphan_symbols = []
+    include_total = 0
+    include_valid = 0
+
+    # 第一轮:建符号表(含命名空间 FQN)
+    for rel, ex in extracted.items():
+        ns = ex['namespace']
+        for c in ex['classes']:
+            symbols[c['name']] = {'file': rel, 'line': c['line']}
+            if ns:
+                fqn_symbols[(ns + '\\' + c['name']).lower()] = rel
+        nodes[rel] = {'file': rel, 'classes': [c['name'] for c in ex['classes']],
+                      'funcs': ex['funcs']}
+
+    def add_edge_if_exists(rel, target_rel, kind, symbol=None):
+        """已存在则加 include 边;否则记悬空(不进图)。"""
+        nonlocal include_total, include_valid, edge_keys
+        # define() 常量链解析可能产生项目内绝对路径 → 归一为相对 rel(保证图谱可移植、query 可遍历)
+        if os.path.isabs(target_rel):
+            normed = os.path.normpath(target_rel)  # 统一分隔符后再判项目内(Windows 下正斜杠/反斜杠)
+            if normed.startswith(os.path.abspath(root)):
+                target_rel = os.path.relpath(normed, os.path.abspath(root)).replace(os.sep, '/')
+            else:
+                return  # 项目外绝对路径,不纳入图谱
+        if kind == 'include':
+            include_total += 1
+        if target_rel and os.path.exists(os.path.join(root, target_rel)):
+            if any(d in target_rel.split('/') for d in _EXCLUDE_DIRS):
+                return
+            if kind == 'include':
+                include_valid += 1
+            key = (rel, target_rel, kind)
+            if key in edge_keys:
+                return
+            edge_keys.add(key)
+            e = {'from': rel, 'to': target_rel, 'kind': kind}
+            if symbol:
+                e['symbol'] = symbol
+            edges.append(e)
+        else:
+            dangling.append({'from': rel, 'raw': target_rel, 'resolved': target_rel,
+                             'base': 'ns' if kind != 'include' else 'src'})
+
+    for rel, ex in extracted.items():
+        for c in ex['classes']:
+            if c.get('extends'):
+                ext = c['extends']
+                # extends 可能是短名(同文件/全局)或 FQN
+                tgt = None
+                if '\\' in ext:
+                    tgt = fqn_symbols.get(ext.lower()) or fqn_to_file(ext, COMPOSER, root)
+                elif ext in symbols:
+                    tgt = symbols[ext]['file']
+                if tgt:
+                    add_edge_if_exists(rel, tgt, 'extends')
+        for inc in ex['includes']:
+            add_edge_if_exists(rel, inc.replace(os.sep, '/'), 'include')
+        # use 导入解析(composer PSR-4 框架核心机制)
+        for u in ex['uses']:
+            fqn = u['fqn']
+            tgt = fqn_symbols.get(fqn.lower()) or fqn_to_file(fqn, COMPOSER, root)
+            if tgt:
+                add_edge_if_exists(rel, tgt, 'use', symbol=fqn)
+        # new \Ns\Class / \Ns\Class:: (框架类实例化/静态调用)
+        for fqn in ex['new_fqns']:
+            tgt = fqn_symbols.get(fqn.lower()) or fqn_to_file(fqn, COMPOSER, root)
+            if tgt and tgt != rel:
+                add_edge_if_exists(rel, tgt, 'autoload', symbol=fqn)
+        # ThinkPHP import()/vendor() 显式类库导入(TP3/TP5,官方 thinkphp.cn/info/126)
+        for func, spec in ex['tp_imports']:
+            if func == 'vendor':
+                spec = 'Vendor.' + spec  # vendor() 说明符隐含 Vendor. 前缀
+            tgt = resolve_tp_import(spec, root, CONST_TABLE)
+            if tgt and tgt != rel:
+                add_edge_if_exists(rel, tgt, 'tpimport', symbol=spec)
+        # 非 PHP 语言模块导入(Django/Flask/Spring/React/Vue/Angular/Next/Nest/Strapi)
+        if ex['lang']:
+            for spec in ex['module_imports']:
+                tgt = resolve_module(spec, ex['lang'], rel, root)
+                if tgt and tgt != rel:
+                    add_edge_if_exists(rel, tgt, 'import', symbol=spec)
+        # 模板 {include file=}(帝国/Dede/Discuz/易优 模板依赖)
+        for tpl in ex['tpl_includes']:
+            trel = os.path.normpath(tpl.lstrip('/'))
+            if not (trel.startswith('/') or re.match(r'^[A-Za-z]:', trel)):
+                trel = os.path.normpath(os.path.join(os.path.dirname(rel), trel))
+            add_edge_if_exists(rel, trel.replace(os.sep, '/'), 'template')
+        # CSS @import 依赖(样式级联,指向其它样式文件)
+        for imp in ex['css_imports']:
+            trel = os.path.normpath(os.path.join(os.path.dirname(rel), imp.lstrip('/')))
+            add_edge_if_exists(rel, trel.replace(os.sep, '/'), 'cssimport')
+        # HTML <link href> / <script src> 资源依赖(样式表与脚本)
+        for a in ex['html_assets']:
+            if a.startswith(('/', 'http://', 'https://', '//', '#', 'data:')):
+                continue  # 绝对/外链不进项目图
+            trel = os.path.normpath(os.path.join(os.path.dirname(rel), a))
+            add_edge_if_exists(rel, trel.replace(os.sep, '/'), 'asset')
+        for call in ex['calls']:
+            # 跨文件调用边(仅当目标符号在别处定义)
+            if call in symbols and symbols[call]['file'] != rel:
+                key = (rel, symbols[call]['file'], 'calls')
+                if key not in edge_keys:
+                    edge_keys.add(key)
+                    edges.append({'from': rel, 'to': symbols[call]['file'],
+                                  'kind': 'calls', 'symbol': call})
+
+    # 孤儿检测(规划 §5.6② 仅告警不裁决):定义了但无人 extends/use/calls 引用的符号
+    referenced = set()
+    for ex in extracted.values():
+        for c in ex['classes']:
+            if c.get('extends'):
+                referenced.add(c['extends'].lstrip('\\').split('\\')[-1].lower())
+        for u in ex['uses']:
+            referenced.add(u.split('\\')[-1].lower())
+        for call in ex['calls']:
+            referenced.add(call.lower())
+    seen_orphan = set()
+    for name, loc in symbols.items():
+        short = name.split('\\')[-1].lower()
+        if short not in referenced:
+            key = (loc['file'], loc.get('line', 0), short)
+            if key not in seen_orphan:
+                seen_orphan.add(key)
+                orphan_symbols.append({'name': name, 'file': loc['file'], 'line': loc.get('line', 0)})
+
+    graph = {'nodes': nodes, 'edges': edges}
+    meta = {
+        'built_at': __import__('datetime').datetime.now().isoformat(),
+        'tool_version': '1.0.0',
+        'root': root,
+        'file_hashes': {f: file_hash(os.path.join(root, f)) for f in file_list},
+        'file_mtimes': {f: _safe_mtime(os.path.join(root, f)) for f in file_list},
+        'lsp_available': lsp,
+        'accuracy_report': {
+            'edges_total': len(edges),
+            'edges_valid': len([e for e in edges if os.path.exists(
+                os.path.join(root, e['to']))]) if edges else 0,
+            'edges_dangling': len(dangling),
+            'symbols_total': len(symbols),
+            'symbols_resolved': len(symbols),
+            'symbols_orphan': len(orphan_symbols),
+            'static_coverage': round(include_valid / include_total, 3) if include_total else 1.0,
+            'include_total': include_total,
+            'include_valid': include_valid,
+            'lsp_available': lsp,
+        },
+        'dangling_edges': dangling,
+        'orphan_symbols': orphan_symbols,
+        'script_hash': file_hash(os.path.abspath(__file__)),
+        # composer.json 哈希:autoload 映射改了 must 重建(extends/use 边依赖 PSR-4 映射)
+        'composer_hash': file_hash(os.path.join(root, 'composer.json'))
+            if os.path.isfile(os.path.join(root, 'composer.json')) else None,
+    }
+    # 原子写(规划 §5.5)
+    _atomic_write(os.path.join(out_dir, 'graph.json'), graph)
+    _atomic_write(os.path.join(out_dir, 'meta.json'), meta)
+    _atomic_write(os.path.join(out_dir, 'symbols.json'), symbols)
+    _write_markdown(os.path.join(out_dir, 'graph.md'), graph, root)
+    print(f"[GRAPH-ACCURACY] 有效边 {meta['accuracy_report']['edges_valid']} / "
+          f"悬空 {meta['accuracy_report']['edges_dangling']}(已剔除)/ "
+          f"include解析率 {meta['accuracy_report']['static_coverage']} / LSP: "
+          f"{','.join(k for k, v in lsp.items() if v) or 'none'}")
+    _release_lock(root)
+    return graph, meta, symbols
+
+
+def _atomic_write(path, obj):
+    tmp = path + '.tmp'
+    with open(tmp, 'w', encoding='utf-8') as f:
+        json.dump(obj, f, ensure_ascii=False, indent=2)
+    os.replace(tmp, path)
+
+
+def _write_markdown(path, graph, root):
+    """graph.md 人类兜底(agent 不加载,规划 §10)。"""
+    lines = ['# 项目结构图谱(人类可读兜底,agent 不加载)', '']
+    for rel, n in graph['nodes'].items():
+        lines.append(f"- {rel}  [classes: {', '.join(n['classes']) or '-'}]")
+    lines.append('')
+    lines.append('## 边')
+    for e in graph['edges']:
+        lines.append(f"- {e['from']} --{e['kind']}--> {e['to']}")
+    with open(path, 'w', encoding='utf-8') as f:
+        f.write('\n'.join(lines))
+
+
+def query(graph, meta, symbols, root, q, direction='both', depth=2, all_cycles=False, max_nodes=80):
+    """§9 查询接口 + 环检测,返回裁剪子图。
+
+    all_cycles: False=仅检测 direction 可达闭包内的环(与「只警告已遍历子图」一致,
+    down=依赖闭包/up=被依赖闭包互不越界);True=对全图做有向 DFS 环检测,任意查询全局预警循环依赖
+    (--direction down 也能报依赖间环)。默认行为不变,E2E-8 不破坏。
+    """
+    # 解析入口
+    entry = None
+    if q in graph['nodes']:
+        entry = q
+    elif q in symbols:
+        entry = symbols[q]['file']
+    else:
+        # 尝试按文件名匹配
+        for rel in graph['nodes']:
+            if rel.endswith('/' + q) or rel == q:
+                entry = rel
+                break
+    if not entry:
+        print(f"[GRAPH-QUERY] 符号索引缺失:{q}(返回空子图,未做全图扫描)")
+        return {'nodes': {}, 'edges': [], 'cycle': 0}
+
+    # 构建邻接(按 direction)+ 平行边 kind 聚合(同 from→to 多种 kind 全保留),(from,to) 去重防重复遍历。
+    adj = {'up': {}, 'down': {}}
+    edge_kind_map = {}  # (from,to) -> 该有向边的全部 kind 列表(平行边聚合)
+    seen_pair = set()
+    for e in graph['edges']:
+        key = (e['from'], e['to'])
+        if key not in seen_pair:
+            seen_pair.add(key)
+            if e['to'] not in adj['down'].setdefault(e['from'], []):
+                adj['down'][e['from']].append(e['to'])
+            if e['from'] not in adj['up'].setdefault(e['to'], []):
+                adj['up'][e['to']].append(e['from'])
+        edge_kind_map.setdefault(key, [])
+        if e['kind'] not in edge_kind_map[key]:
+            edge_kind_map[key].append(e['kind'])
+
+    MAX_NODES = max_nodes  # 节点预算硬上限(规划 §9 省 token:防枢纽模块 2 跳爆炸)
+    visited = set()    # 全局去重:该节点已纳入子图(不论是否在当前递归路径)
+    stack = set()      # 当前递归路径(仅用于真环判定的回边检测)
+    subgraph_nodes = {}
+    subgraph_edges = []
+    cycles = [0]
+    seen_cycle = set()  # 环边按 (from,to) 去重,仅保留一条(规划 §9「环只保留一条边」)
+    # 环检测:在「有向(down)图」独立 DFS 回边计数,与查询方向解耦,保证守恒②③①:
+    # ② 回边须沿 authored 方向(down)闭合,禁止 both/up 反向命中祖先的伪回边(菱形 DAG 假阳性);
+    # ① 自环 A→A 在有向 DFS 中只计 1 次(旧 both 模式 up/down 邻接各含 A 会计 2)。
+    # 范围=direction 可达闭包(非仅 entry 的 down 集),否则 up/both 漏检 entry 纯上游环。
+    def _reachable(entry, direction):
+        dirs = []
+        if direction in ('up', 'both'):
+            dirs.append('up')
+        if direction in ('down', 'both'):
+            dirs.append('down')
+        seen = {entry}
+        stack = [entry]
+        while stack:
+            u = stack.pop()
+            for dr in dirs:
+                for v in adj[dr].get(u, []):
+                    if v not in seen:
+                        seen.add(v)
+                        stack.append(v)
+        return seen
+
+    def detect_cycles(starts, allowed):
+        vis = set(); st = set(); pairs = []
+        def dfs(u):
+            vis.add(u); st.add(u)
+            for v in adj['down'].get(u, []):
+                if v not in allowed:      # 不走出查询遍历范围,避免把范围外下游环算进来
+                    continue
+                if v in st:
+                    pairs.append((u, v))   # u→v 沿 authored 方向闭合环
+                elif v not in vis:
+                    dfs(v)
+            st.discard(u)
+        for s in starts:
+            if s not in vis:
+                dfs(s)
+        return pairs
+    # 全局环检测模式:起点/范围=全图节点(不受 direction 闭包限制);默认模式=遍历闭包
+    if all_cycles:
+        cyc_scope = set(graph['nodes'])
+    else:
+        cyc_scope = _reachable(entry, direction)
+    cyc_pairs = detect_cycles(cyc_scope, cyc_scope)
+    cyc_set = set(cyc_pairs)
+    cycles[0] = len(cyc_pairs)
+
+    def walk(node, d, came_from=None, came_dir=None):
+        if node in visited:
+            return
+        visited.add(node)
+        stack.add(node)
+        if node in graph['nodes']:
+            subgraph_nodes[node] = graph['nodes'][node]
+        if d >= depth or len(subgraph_nodes) >= MAX_NODES:
+            stack.discard(node)
+            return
+        dirs = []
+        if direction in ('up', 'both'):
+            dirs.append('up')
+        if direction in ('down', 'both'):
+            dirs.append('down')
+        for dr in dirs:
+            for nxt in adj[dr].get(node, []):
+                if len(subgraph_nodes) >= MAX_NODES:
+                    return
+                # both 模式"刚走过的同一条边的逆向遍历"是伪环(A→B 再 up 回 A);
+                # 仅 nxt==came_from 且 dr!=came_dir 才跳过。真 2 节点互环两向相同(dr==came_dir),不算伪环。
+                if nxt == came_from and dr != came_dir:
+                    continue
+                # 仅「有向图 DFS 真回边」(node,nxt)∈cyc_set 且在当前路径上才算环;
+                # 菱形/重复边目标节点虽 visited 但非 stack 上的 down 回边→不计。
+                if nxt in stack and dr == 'down' and (node, nxt) in cyc_set:
+                    # 环边引用的祖先节点可能不在子图节点集,补入避免人类兜底 graph.md 引用缺失(B-2)
+                    if nxt in graph['nodes']:
+                        subgraph_nodes.setdefault(nxt, graph['nodes'][nxt])
+                    if (node, nxt) not in seen_cycle:
+                        seen_cycle.add((node, nxt))
+                        subgraph_edges.append({'from': node, 'to': nxt,
+                                               'kind': 'cycle'})
+                    continue
+                # 平行边:取 (node,nxt) 或 (nxt,node) 的全部 kind,逐条产出(不覆盖)
+                kinds = edge_kind_map.get((node, nxt)) or edge_kind_map.get(
+                    (nxt, node)) or ['include']
+                for k in kinds:
+                    subgraph_edges.append({'from': node, 'to': nxt, 'kind': k})
+                walk(nxt, d + 1, came_from=node, came_dir=dr)
+        stack.discard(node)
+
+    walk(entry, 0)
+    if cycles[0]:
+        print(f"[GRAPH-CYCLE] 检测到 {cycles[0]} 个环(已在子图中折叠)")
+    return {'nodes': subgraph_nodes, 'edges': subgraph_edges, 'cycle': cycles[0]}
+
+
+def _graph_files_present(root):
+    """B1:图谱三件套(graph/meta/symbols)是否齐全。任一缺失 → query 入口须重建,避免 open 崩溃。"""
+    kg = os.path.join(root, '.ai-memory', 'knowledge-graph')
+    return all(os.path.isfile(os.path.join(kg, f))
+               for f in ('graph.json', 'meta.json', 'symbols.json'))
+
+
+def need_rebuild(root):
+    """规划 §6 新鲜度:比对 mtime+内容哈希。
+
+    返回 (stale, changed_files):
+    - changed_files: set of changed file rels(增量提示),None 表示需全量重建
+      (脚本升级/composer.json 变更/文件删除/产物缺失等无法增量的情况)。
+    注意:即便返回 changed_files,build 仍全量 extract——因 CONST_TABLE 全局依赖
+    (改 A.php 的 define 会影响 B.php 的 include 解析),extract 增量风险过高,留作 v2。
+    """
+    meta_path = os.path.join(root, '.ai-memory', 'knowledge-graph', 'meta.json')
+    if not os.path.exists(meta_path):
+        return True, None
+    try:
+        with open(meta_path, encoding='utf-8') as f:
+            meta = json.load(f)
+    except Exception:
+        return True, None
+    changed_files = set()
+    current_rels = set()  # B3:记录现存源文件,检测"删除"
+    mtimes = meta.get('file_mtimes', {})  # 廉价 mtime 前置过滤:mtime 未变则内容必未变
+    for dirpath, dirnames, filenames in os.walk(root):
+        dirnames[:] = [d for d in dirnames if d not in _EXCLUDE_DIRS
+                       and not d.startswith('.')]
+        for fn in filenames:
+            if fn.endswith(SRC_EXTS):
+                p = os.path.join(dirpath, fn)
+                rel = os.path.relpath(p, root).replace(os.sep, '/')
+                current_rels.add(rel)
+                # mtime 一致(旧版无 file_mtimes 时 mtimes={} → 回退全量 md5)
+                if rel in mtimes and mtimes[rel] == _safe_mtime(p):
+                    continue
+                h = file_hash(p)
+                if rel not in meta.get('file_hashes', {}) or meta['file_hashes'][rel] != h:
+                    changed_files.add(rel)
+    # B3:meta 记录的文件已不在当前树(被删)→ 无法增量,返回 None
+    if set(meta.get('file_hashes', {})) - current_rels:
+        return True, None
+    # 脚本版本/composer.json 变化:无法增量,返回 None
+    if meta.get('script_hash') != file_hash(os.path.abspath(__file__)):
+        return True, None
+    cj = os.path.join(root, 'composer.json')
+    if os.path.isfile(cj):
+        if file_hash(cj) != meta.get('composer_hash'):
+            return True, None
+    elif meta.get('composer_hash') is not None:
+        return True, None
+    return len(changed_files) > 0, changed_files
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    ap.add_argument('--root', default=os.getcwd())
+    ap.add_argument('--query', help='入口 file 或 symbol,多个逗号分隔合并子图')
+    ap.add_argument('--direction', default='both', choices=['up', 'down', 'both'])
+    ap.add_argument('--depth', type=int, default=2)
+    ap.add_argument('--rebuild', action='store_true', help='强制全量重建')
+    ap.add_argument('--no-rebuild', action='store_true',
+                    help='跳过新鲜度检测,直接复用已缓存图谱(仅在确认图谱新鲜的连续查询时使用)')
+    ap.add_argument('--all-cycles', action='store_true',
+                    help='全局环检测:对任意查询都全图 DFS 预警循环依赖(不受 --direction 闭包限制),默认关')
+    ap.add_argument('--exclude', default=None,
+                    help='追加排除目录(逗号分隔),合并到默认排除 + .graphignore')
+    ap.add_argument('--max-nodes', type=int, default=80,
+                    help='查询子图节点预算硬上限(默认 80,防枢纽模块爆炸)')
+    ap.add_argument('--selftest', action='store_true',
+                    help='运行内置 E2E 自检(删文件无悬挂边 + 改文件过期判定),不改交付物')
+    args = ap.parse_args()
+    root = os.path.abspath(args.root)
+    # 初始化运行时排除集(合并默认 + .graphignore + --exclude)
+    _EXCLUDE_DIRS.clear()
+    _EXCLUDE_DIRS.update(load_exclude_dirs(root, args.exclude))
+    if args.selftest:
+        import tempfile as _tf
+        import shutil as _shutil
+        # B-3:在副本运行,避免触碰用户原始交付物(旧版会 rename/改写原文件)
+        work = _tf.mkdtemp(prefix='kg_selftest_')
+        _shutil.copytree(root, work, dirs_exist_ok=True,
+                         ignore=_shutil.ignore_patterns('.ai-memory', '.codebuddy', '.git'))
+        kg = os.path.join(work, '.ai-memory', 'knowledge-graph')
+        def load():
+            return (json.load(open(os.path.join(kg, 'graph.json'), encoding='utf-8')),
+                    json.load(open(os.path.join(kg, 'meta.json'), encoding='utf-8')),
+                    json.load(open(os.path.join(kg, 'symbols.json'), encoding='utf-8')))
+        build(work)
+        g, m, s = load()
+        # E2E-5: 注释伪符号回归(build_graph 注释缺陷防护,ERR-006)。
+        # 构造最小 PHP fixture:真实符号 + 注释/死代码中的伪符号,build 后断言
+        # symbols.json 不含伪符号、edges 不含指向伪符号的假 extends/calls 边。
+        # 须置于 victim 早返回(非 include 项目会早退)之前,确保任何项目均跑此断言。
+        fx_dir = os.path.join(work, '__kg_phantom_fixture__')
+        os.makedirs(fx_dir, exist_ok=True)
+        fx = os.path.join(fx_dir, 'phantom.php')
+        with open(fx, 'w', encoding='utf-8') as _f:
+            _f.write(
+                "<?php\n"
+                "// class KgTestPhantomCls extends RealClass\n"   # 行注释伪类
+                "// KgTestPhantomHelper::go();\n"                 # 行注释伪静态调用
+                "/* class KgTestPhantomDead {} */\n"              # 块注释伪类
+                "class RealClass {}\n"                        # 真实类
+                "function realFunc() {}\n"                    # 真实函数
+                "$o = new RealClass();\n"                     # 真实实例化
+                "RealClass::const();\n"                       # 真实静态调用
+            )
+        build(work)
+        g5, _, s5 = load()
+        phantom_names = ('KgTestPhantomCls', 'KgTestPhantomDead', 'KgTestPhantomHelper')
+        # 仅断言 fixture 文件自身的 symbols:build() 会扫描 build_graph.py 自身,
+        # 其源码内嵌的 fixture 字符串字面量里 `class Xxx` 会被 RE_CLASS 误抽,
+        # 故须按文件归属过滤,避免自扫描导致的假阳性(非代码缺陷)。
+        fx_rel = '__kg_phantom_fixture__/phantom.php'
+        fx_syms = [n for n, loc in s5.items()
+                   if isinstance(loc, dict) and loc.get('file') == fx_rel]
+        phantom_syms = [n for n in phantom_names if n in fx_syms]
+        print('[E2E-5] 伪符号命中:', phantom_syms, '(期望 [])')
+        assert not phantom_syms, '[E2E-5] 注释伪符号未过滤: %s' % phantom_syms
+        # 真实符号须保留(避免 strip 误伤正常代码)。须断言 fixture 文件自身 fx_syms 而非全局 s5:
+        # build() 会扫描自身(.py),其内嵌 fixture 字符串里的 `class RealClass` 曾被 RE_CLASS 误抽进 s5 兜底,
+        # 用全局 s5 会使本断言"假通过"——即使 fixture 真实符号被误剥,s5 仍有同名符号。
+        assert 'RealClass' in fx_syms, '[E2E-5] fixture 真实符号 RealClass 被误剥(fx_syms 缺失)'
+        # edges 不得含指向伪符号的 symbol(extends/calls/autoload 均经 symbols 命中,伪符号不在表中则无假边)
+        phantom_edges = [e for e in g5['edges']
+                         if e.get('symbol') in phantom_names]
+        print('[E2E-5] 指向伪符号的假边:', len(phantom_edges), '(期望 0)')
+        assert not phantom_edges, '[E2E-5] 存在指向注释伪符号的假边'
+        print('[E2E-5] 通过:注释伪符号已过滤,真实符号保留')
+        # E2E-6: strip_php_comments 须保留换行(ERR-006 同族),否则真实符号行号上移、改动定位失真。
+        _s6_in = (
+            "<?php\n"
+            "class A {}\n"        # line 2
+            "/* multi\n"          # line 3
+            "   line comment\n"    # line 4
+            "*/\n"                # line 5
+            "class B {}\n"        # line 6(strip 后仍须在第 6 行)
+        )
+        _s6_out = strip_php_comments(_s6_in)
+        _s6_nl_in = _s6_in.count('\n')
+        _s6_nl_out = _s6_out.count('\n')
+        print('[E2E-6] 换行数 输入/输出:', _s6_nl_in, '/', _s6_nl_out, '(期望相等)')
+        assert _s6_nl_out == _s6_nl_in, \
+            '[E2E-6] 块注释删除内部换行致行号位移:去 %d 行' % (_s6_nl_in - _s6_nl_out)
+        assert 'class B' in _s6_out, '[E2E-6] 真实符号 B 被块注释误吞'
+        print('[E2E-6] 通过:块注释内部换行已保留,行号不位移')
+        # E2E-7: 符号抽取语言隔离(issue-2)。非 PHP 文件不得被 PHP 正则误抽;PHP 文件仍须正常抽取。
+        _py_f = os.path.join(work, '__kg_lang_iso__.py')
+        with open(_py_f, 'w', encoding='utf-8') as _f:
+            _f.write("class FakeCls:\n    x = new RealCls()\n")
+        _js_f = os.path.join(work, '__kg_lang_iso__.js')
+        with open(_js_f, 'w', encoding='utf-8') as _f:
+            _f.write("class KgTestPhantomCls { go() { this.helper() } }\n")
+        _php_f = os.path.join(work, '__kg_lang_iso__.php')
+        with open(_php_f, 'w', encoding='utf-8') as _f:
+            _f.write("<?php\nclass RealPhpCls {}\n")
+        _ex_py = extract_file(_py_f, work)
+        _ex_js = extract_file(_js_f, work)
+        _ex_php = extract_file(_php_f, work)
+        _py_classes = [c['name'] for c in _ex_py['classes']]
+        _js_classes = [c['name'] for c in _ex_js['classes']]
+        print('[E2E-7] .py 误抽 class:', _py_classes, '(期望 [])')
+        print('[E2E-7] .js 误抽 class:', _js_classes, '(期望 [])')
+        assert not _py_classes, '[E2E-7] .py 被误抽 PHP 符号: %s' % _py_classes
+        assert not _js_classes, '[E2E-7] .js 被误抽 PHP 符号: %s' % _js_classes
+        # PHP 抽取未被连带禁用(回归:非 PHP 门禁不能误伤 PHP)
+        _php_classes = [c['name'] for c in _ex_php['classes']]
+        assert 'RealPhpCls' in _php_classes, \
+            '[E2E-7] PHP 真实类未被抽取(门禁误伤): %s' % _php_classes
+        print('[E2E-7] 通过:非 PHP 零误抽,PHP 抽取保留')
+        # E2E-8: 环检测三守恒回归(历史犯错最多,karpathy 守恒 + ERR-004):
+        # ① 计数不重不漏 ② 遍历方向与边有向性一致 ③ 平行边不翻倍。
+        # 直接构造图 dict 调 query()(免建文件),覆盖 7 例(菱形DAG/3环/互环2/自环/平行边/3环(up)/上游环(up))。
+        def _mk_g(nodes, edges):
+            return {'nodes': {n: {} for n in nodes},
+                    'edges': [{'from': a, 'to': b, 'kind': 'include'} for a, b in edges]}
+        _cyc_cases = [
+            # (name, nodes, edges, direction, expect_cycle)  — 期望见 karpathy 守恒
+            ('菱形DAG(both)', ['A', 'B', 'C', 'D'],
+             [('A', 'B'), ('A', 'C'), ('B', 'D'), ('C', 'D')], 'both', 0),   # 守恒②:反向遍历命中祖先不得算环
+            ('真实3环(both)', ['A', 'B', 'C'],
+             [('A', 'B'), ('B', 'C'), ('C', 'A')], 'both', 1),                # 守恒①②
+            ('互环2(both)', ['A', 'B'],
+             [('A', 'B'), ('B', 'A')], 'both', 1),                            # 守恒①②
+            ('自环(both)', ['A'], [('A', 'A')], 'both', 1),                   # 守恒①:旧实现计 2,须 1
+            ('平行边(both)', ['A', 'B'],
+             [('A', 'B'), ('A', 'B')], 'both', 0),                            # 守恒③:平行边不翻倍
+            ('真实3环(up)', ['A', 'B', 'C'],
+             [('A', 'B'), ('B', 'C'), ('C', 'A')], 'up', 1),                  # 守恒②:up 模式仍须检出真环
+            ('上游环(up)', ['X', 'Y', 'Z'],
+             [('Y', 'X'), ('Y', 'Z'), ('Z', 'Y')], 'up', 1),                  # 守恒②:entry 纯上游 Y↔Z 环须检出(entry 不在环上)
+        ]
+        for _cn, _nd, _ed, _dir, _exp in _cyc_cases:
+            _gr = _mk_g(_nd, _ed)
+            _r = query(_gr, {}, {}, work, _nd[0], direction=_dir, depth=5)
+            print('[E2E-8] %s 环数: %s (期望 %s)' % (_cn, _r['cycle'], _exp))
+            assert _r['cycle'] == _exp, \
+                '[E2E-8] %s 环数=%s 期望 %s(守恒违反)' % (_cn, _r['cycle'], _exp)
+        print('[E2E-8] 通过:环检测三守恒(不重不漏/方向一致/平行边不翻倍)均满足')
+        # E2E-9: 全局环检测开关(改进项1)。图 A↔B 成环、A→X(X 下游汇点);
+        # entry=X --direction down 默认只遍历闭包 {X} → 0 环;--all-cycles 全图 DFS → 命中 A↔B → 1 环。
+        _g9 = _mk_g(['A', 'B', 'X'], [('A', 'B'), ('B', 'A'), ('A', 'X')])
+        _r9_def = query(_g9, {}, {}, work, 'X', direction='down', depth=5, all_cycles=False)
+        print('[E2E-9] 默认(down,entry=X) 环数: %s (期望 0)' % _r9_def['cycle'])
+        assert _r9_def['cycle'] == 0, '[E2E-9] 默认模式误报闭包外环: %s' % _r9_def['cycle']
+        _r9_all = query(_g9, {}, {}, work, 'X', direction='down', depth=5, all_cycles=True)
+        print('[E2E-9] 全局(--all-cycles,down,entry=X) 环数: %s (期望 1)' % _r9_all['cycle'])
+        assert _r9_all['cycle'] == 1, '[E2E-9] 全局模式漏报全图环: %s' % _r9_all['cycle']
+        print('[E2E-9] 通过:全局环检测开关生效且不破坏默认闭包行为')
+        # E2E-3: 选一个被依赖的真实源文件,删之,重建后其依赖边应不残留(悬空已剔除)
+        victim = None
+        for e in g['edges']:
+            if e['kind'] == 'include' and os.path.exists(os.path.join(work, e['to'])):
+                victim = e['to']
+                break
+        if not victim:
+            # 非 PHP/JS include 项目(纯 import/use、空图谱等)无可用 victim,
+            # 优雅跳过并清理临时副本,避免 AssertionError 崩溃 + 残留目录。
+            _shutil.rmtree(work)
+            print('[SELFTEST] 跳过 E2E-3/4:当前目录图谱无 include 边(非 PHP/JS include 项目),无法构造 victim(副本已清理)')
+            return
+        dependents = [e['from'] for e in g['edges'] if e['to'] == victim]
+        print('[E2E-3] 依赖', victim, '的文件数:', len(dependents))
+        bak = os.path.join(work, victim) + '.baktest'
+        os.rename(os.path.join(work, victim), bak)
+        build(work)
+        g2, _, _ = load()
+        hanging = [e for e in g2['edges'] if e['to'] == victim]
+        print('[E2E-3] 删除后悬挂边:', len(hanging), '(期望 0)')
+        os.rename(bak, os.path.join(work, victim))
+        # E2E-4: 改一个真实文件,need_rebuild 应报 stale
+        orig = open(os.path.join(work, victim), encoding='utf-8', errors='ignore').read()
+        open(os.path.join(work, victim), 'a', encoding='utf-8').write('\n// e2e touch\n')
+        stale, changed_info = need_rebuild(work)
+        cnt = len(changed_info) if changed_info else '全量'
+        print('[E2E-4] 编辑后过期:', stale, '变更文件数:', cnt, '(期望 True/>0)')
+        open(os.path.join(work, victim), 'w', encoding='utf-8').write(orig)
+        _shutil.rmtree(work)
+        print('[SELFTEST] 完成(副本已清理,原交付物未改动)')
+        return
+    kg_dir = os.path.join(root, '.ai-memory', 'knowledge-graph')
+
+    # 冷启动 / 新鲜度(规划 §6)
+    if args.rebuild:
+        build(root)
+        return
+    if args.query:
+        if args.no_rebuild and _graph_files_present(root):
+            print('[GRAPH-USED-CACHE] 跳过新鲜度检测,复用已缓存图谱')
+        else:
+            stale, changed_info = need_rebuild(root)
+            # B1:meta.json 在但 graph.json/symbols.json 缺失(被误删/损坏)→ 即便源码未变也必须重建,
+            # 否则下方直接 open(graph.json) 会抛 FileNotFoundError 崩溃。
+            if stale or not _graph_files_present(root):
+                if changed_info:
+                    preview = ', '.join(sorted(changed_info)[:5])
+                    suffix = '…' if len(changed_info) > 5 else ''
+                    print(f"[GRAPH-STALE] {len(changed_info)} 个文件变更:{preview}{suffix},正在重建…")
+                else:
+                    print("[GRAPH-STALE] 图谱过期(产物缺失/脚本升级/composer.json 变更),正在全量重建…")
+                build(root)  # 全量重抽保准确性(extract 增量因 CONST_TABLE 依赖风险留作 v2)
+            else:
+                print("[GRAPH-FRESH] 图谱新鲜")
+        with open(os.path.join(kg_dir, 'graph.json'), encoding='utf-8') as f:
+            graph = json.load(f)
+        with open(os.path.join(kg_dir, 'meta.json'), encoding='utf-8') as f:
+            meta = json.load(f)
+        with open(os.path.join(kg_dir, 'symbols.json'), encoding='utf-8') as f:
+            symbols = json.load(f)
+        # P1:支持多入口(逗号分隔),合并子图——覆盖 SKILL.md Step2 P1「--query <改动文件1,改动文件2,…>」
+        # 查依赖闭包的使用方式;单入口时退化为原行为。
+        merged = {'nodes': {}, 'edges': [], 'cycle': 0}
+        for q in args.query.split(','):
+            q = q.strip()
+            if not q:
+                continue
+            sub = query(graph, meta, symbols, root, q, args.direction, args.depth,
+                        args.all_cycles, args.max_nodes)
+            merged['nodes'].update(sub['nodes'])
+            merged['edges'].extend(sub['edges'])
+            merged['cycle'] += sub['cycle']
+        print(json.dumps(merged, ensure_ascii=False))
+        return
+    # 默认:全量构建
+    build(root)
+
+
+if __name__ == '__main__':
+    main()

+ 138 - 0
.claude/skills/diagnosing-bugs/SKILL.md

@@ -0,0 +1,138 @@
+---
+name: diagnosing-bugs
+description: Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.
+---
+
+# Diagnosing Bugs
+
+A discipline for hard bugs. Skip phases only when explicitly justified.
+
+When exploring the codebase, read `CONTEXT.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
+
+## Redact
+
+This skill has you show commands, outputs and captured artifacts. **Redact every secret first** — write `<REDACTED>` in its place. Build loops against env vars, so the credential stays in the environment rather than in what you show. Captured artifacts carry auth headers: quote only the lines that carry the signal.
+
+If the redacted output is not enough to diagnose the bug, say so and ask the user.
+
+## Phase 1 — Build a feedback loop
+
+**This is the skill.** Everything else is mechanical. If you have a **tight** pass/fail signal for the bug — one that goes red on _this_ bug — you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you.
+
+Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.**
+
+### Ways to construct one — try them in roughly this order
+
+1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e.
+2. **Curl / HTTP script** against a running dev server.
+3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot.
+4. **Headless browser script** (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network.
+5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation.
+6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call.
+7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
+8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it.
+9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs.
+10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you.
+
+Build the right feedback loop, and the bug is 90% fixed.
+
+### Tighten the loop
+
+Treat the loop as a product. Once you have _a_ loop, **tighten** it:
+
+- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.)
+- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".)
+- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.)
+
+A 30-second flaky loop is barely better than no loop; a 2-second deterministic one is tight — a debugging superpower.
+
+### Non-deterministic bugs
+
+The goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable.
+
+### When you genuinely cannot build a loop
+
+Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a redacted captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop.
+
+### Completion criterion — a tight loop that goes red
+
+Phase 1 is done when the loop is **tight** and **red-capable**: you can name **one command** — a script path, a test invocation, a curl — that you have **already run at least once** (show the invocation and its output, redacted), and that is:
+
+- [ ] **Red-capable** — it drives the actual bug code path and asserts the **user's exact symptom**, so it can go red on this bug and green once fixed. Not "runs without erroring" — it must be able to _catch this specific bug_.
+- [ ] **Deterministic** — same verdict every run (flaky bugs: a pinned, high reproduction rate, per above).
+- [ ] **Fast** — seconds, not minutes.
+- [ ] **Agent-runnable** — you can run it unattended; a human in the loop only via `scripts/hitl-loop.template.sh`.
+
+If you catch yourself reading code to build a theory before this command exists, **stop — jumping straight to a hypothesis is the exact failure this skill prevents.** No red-capable command, no Phase 2.
+
+## Phase 2 — Reproduce + minimise
+
+Run the loop. Watch it go red — the bug appears.
+
+Confirm:
+
+- [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix.
+- [ ] The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against).
+- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it.
+
+### Minimise
+
+Once it's red, shrink the repro to the **smallest scenario that still goes red**. Cut inputs, callers, config, data, and steps **one at a time**, re-running the loop after each cut — keep only what's load-bearing for the failure.
+
+Why bother: a minimal repro shrinks the hypothesis space in Phase 3 (fewer moving parts left to suspect) and becomes the clean regression test in Phase 5.
+
+Done when **every remaining element is load-bearing** — removing any one of them makes the loop go green.
+
+Do not proceed until you have reproduced **and** minimised.
+
+## Phase 3 — Hypothesise
+
+Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea.
+
+Each hypothesis must be **falsifiable**: state the prediction it makes.
+
+> Format: "If <X> is the cause, then <changing Y> will make the bug disappear / <changing Z> will make it worse."
+
+If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it.
+
+**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK.
+
+## Phase 4 — Instrument
+
+Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.**
+
+Tool preference:
+
+1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs.
+2. **Targeted logs** at the boundaries that distinguish hypotheses.
+3. Never "log everything and grep".
+
+**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die.
+
+**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second.
+
+## Phase 5 — Fix + regression test
+
+Write the regression test **before the fix** — but only if there is a **correct seam** for it.
+
+A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence.
+
+**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase.
+
+If a correct seam exists:
+
+1. Turn the minimised repro into a failing test at that seam.
+2. Watch it fail.
+3. Apply the fix.
+4. Watch it pass.
+5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario.
+
+## Phase 6 — Cleanup
+
+Required before declaring done:
+
+- [ ] Original repro no longer reproduces (re-run the Phase 1 loop)
+- [ ] Regression test passes (or absence of seam is documented)
+- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix)
+- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location)
+- [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns

+ 3 - 0
.claude/skills/diagnosing-bugs/agents/openai.yaml

@@ -0,0 +1,3 @@
+interface:
+  display_name: "Diagnosing Bugs"
+  short_description: "Diagnose hard bugs and regressions"

+ 44 - 0
.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh

@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# Human-in-the-loop reproduction loop.
+# Copy this file, edit the steps below, and run it.
+# The agent runs the script; the user follows prompts in their terminal.
+#
+# Usage:
+#   bash hitl-loop.template.sh
+#
+# Two helpers:
+#   step "<instruction>"          → show instruction, wait for Enter
+#   capture VAR "<question>"      → show question, read response into VAR
+#
+# At the end, captured values are printed as KEY=VALUE for the agent to parse.
+#
+# `capture` prints its value back to the terminal, where the agent reads it — so
+# capture observations, and leave signing in to the user as a `step`.
+
+set -euo pipefail
+
+step() {
+  printf '\n>>> %s\n' "$1"
+  read -r -p "    [Enter when done] " _
+}
+
+capture() {
+  local var="$1" question="$2" answer
+  printf '\n>>> %s\n' "$question"
+  read -r -p "    > " answer
+  printf -v "$var" '%s' "$answer"
+}
+
+# --- edit below ---------------------------------------------------------
+
+step "Open the app at http://localhost:3000 and sign in."
+
+capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)"
+
+capture ERROR_MSG "Paste the error message (or 'none'):"
+
+# --- edit above ---------------------------------------------------------
+
+printf '\n--- Captured ---\n'
+printf 'ERRORED=%s\n' "$ERRORED"
+printf 'ERROR_MSG=%s\n' "$ERROR_MSG"

+ 182 - 0
.claude/skills/dispatching-parallel-agents/SKILL.md

@@ -0,0 +1,182 @@
+---
+name: dispatching-parallel-agents
+description: Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies
+---
+
+# Dispatching Parallel Agents
+
+## Overview
+
+You delegate tasks to specialized agents with isolated context. By precisely crafting their instructions and context, you ensure they stay focused and succeed at their task. They should never inherit your session's context or history — you construct exactly what they need. This also preserves your own context for coordination work.
+
+When you have multiple unrelated failures (different test files, different subsystems, different bugs), investigating them sequentially wastes time. Each investigation is independent and can happen in parallel.
+
+**Core principle:** Dispatch one agent per independent problem domain. Let them work concurrently.
+
+## When to Use
+
+```dot
+digraph when_to_use {
+    "Multiple failures?" [shape=diamond];
+    "Are they independent?" [shape=diamond];
+    "Single agent investigates all" [shape=box];
+    "One agent per problem domain" [shape=box];
+    "Can they work in parallel?" [shape=diamond];
+    "Sequential agents" [shape=box];
+    "Parallel dispatch" [shape=box];
+
+    "Multiple failures?" -> "Are they independent?" [label="yes"];
+    "Are they independent?" -> "Single agent investigates all" [label="no - related"];
+    "Are they independent?" -> "Can they work in parallel?" [label="yes"];
+    "Can they work in parallel?" -> "Parallel dispatch" [label="yes"];
+    "Can they work in parallel?" -> "Sequential agents" [label="no - shared state"];
+}
+```
+
+**Use when:**
+- 3+ test files failing with different root causes
+- Multiple subsystems broken independently
+- Each problem can be understood without context from others
+- No shared state between investigations
+
+**Don't use when:**
+- Failures are related (fix one might fix others)
+- Need to understand full system state
+- Agents would interfere with each other
+
+## The Pattern
+
+### 1. Identify Independent Domains
+
+Group failures by what's broken:
+- File A tests: Tool approval flow
+- File B tests: Batch completion behavior
+- File C tests: Abort functionality
+
+Each domain is independent - fixing tool approval doesn't affect abort tests.
+
+### 2. Create Focused Agent Tasks
+
+Each agent gets:
+- **Specific scope:** One test file or subsystem
+- **Clear goal:** Make these tests pass
+- **Constraints:** Don't change other code
+- **Expected output:** Summary of what you found and fixed
+
+### 3. Dispatch in Parallel
+
+```typescript
+// In Claude Code / AI environment
+Task("Fix agent-tool-abort.test.ts failures")
+Task("Fix batch-completion-behavior.test.ts failures")
+Task("Fix tool-approval-race-conditions.test.ts failures")
+// All three run concurrently
+```
+
+### 4. Review and Integrate
+
+When agents return:
+- Read each summary
+- Verify fixes don't conflict
+- Run full test suite
+- Integrate all changes
+
+## Agent Prompt Structure
+
+Good agent prompts are:
+1. **Focused** - One clear problem domain
+2. **Self-contained** - All context needed to understand the problem
+3. **Specific about output** - What should the agent return?
+
+```markdown
+Fix the 3 failing tests in src/agents/agent-tool-abort.test.ts:
+
+1. "should abort tool with partial output capture" - expects 'interrupted at' in message
+2. "should handle mixed completed and aborted tools" - fast tool aborted instead of completed
+3. "should properly track pendingToolCount" - expects 3 results but gets 0
+
+These are timing/race condition issues. Your task:
+
+1. Read the test file and understand what each test verifies
+2. Identify root cause - timing issues or actual bugs?
+3. Fix by:
+   - Replacing arbitrary timeouts with event-based waiting
+   - Fixing bugs in abort implementation if found
+   - Adjusting test expectations if testing changed behavior
+
+Do NOT just increase timeouts - find the real issue.
+
+Return: Summary of what you found and what you fixed.
+```
+
+## Common Mistakes
+
+**❌ Too broad:** "Fix all the tests" - agent gets lost
+**✅ Specific:** "Fix agent-tool-abort.test.ts" - focused scope
+
+**❌ No context:** "Fix the race condition" - agent doesn't know where
+**✅ Context:** Paste the error messages and test names
+
+**❌ No constraints:** Agent might refactor everything
+**✅ Constraints:** "Do NOT change production code" or "Fix tests only"
+
+**❌ Vague output:** "Fix it" - you don't know what changed
+**✅ Specific:** "Return summary of root cause and changes"
+
+## When NOT to Use
+
+**Related failures:** Fixing one might fix others - investigate together first
+**Need full context:** Understanding requires seeing entire system
+**Exploratory debugging:** You don't know what's broken yet
+**Shared state:** Agents would interfere (editing same files, using same resources)
+
+## Real Example from Session
+
+**Scenario:** 6 test failures across 3 files after major refactoring
+
+**Failures:**
+- agent-tool-abort.test.ts: 3 failures (timing issues)
+- batch-completion-behavior.test.ts: 2 failures (tools not executing)
+- tool-approval-race-conditions.test.ts: 1 failure (execution count = 0)
+
+**Decision:** Independent domains - abort logic separate from batch completion separate from race conditions
+
+**Dispatch:**
+```
+Agent 1 → Fix agent-tool-abort.test.ts
+Agent 2 → Fix batch-completion-behavior.test.ts
+Agent 3 → Fix tool-approval-race-conditions.test.ts
+```
+
+**Results:**
+- Agent 1: Replaced timeouts with event-based waiting
+- Agent 2: Fixed event structure bug (threadId in wrong place)
+- Agent 3: Added wait for async tool execution to complete
+
+**Integration:** All fixes independent, no conflicts, full suite green
+
+**Time saved:** 3 problems solved in parallel vs sequentially
+
+## Key Benefits
+
+1. **Parallelization** - Multiple investigations happen simultaneously
+2. **Focus** - Each agent has narrow scope, less context to track
+3. **Independence** - Agents don't interfere with each other
+4. **Speed** - 3 problems solved in time of 1
+
+## Verification
+
+After agents return:
+1. **Review each summary** - Understand what changed
+2. **Check for conflicts** - Did agents edit same code?
+3. **Run full suite** - Verify all fixes work together
+4. **Spot check** - Agents can make systematic errors
+
+## Real-World Impact
+
+From debugging session (2025-10-03):
+- 6 failures across 3 files
+- 3 agents dispatched in parallel
+- All investigations completed concurrently
+- All fixes integrated successfully
+- Zero conflicts between agent changes

+ 47 - 0
.claude/skills/domain-modeling/ADR-FORMAT.md

@@ -0,0 +1,47 @@
+# ADR Format
+
+ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
+
+Create the `docs/adr/` directory lazily — only when the first ADR is needed.
+
+## Template
+
+```md
+# {Short title of the decision}
+
+{1-3 sentences: what's the context, what did we decide, and why.}
+```
+
+That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections.
+
+## Optional sections
+
+Only include these when they add genuine value. Most ADRs won't need them.
+
+- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
+- **Considered Options** — only when the rejected alternatives are worth remembering
+- **Consequences** — only when non-obvious downstream effects need to be called out
+
+## Numbering
+
+Scan `docs/adr/` for the highest existing number and increment by one.
+
+## When to offer an ADR
+
+All three of these must be true:
+
+1. **Hard to reverse** — the cost of changing your mind later is meaningful
+2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
+3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
+
+If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
+
+### What qualifies
+
+- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
+- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
+- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
+- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
+- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
+- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
+- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.

+ 60 - 0
.claude/skills/domain-modeling/CONTEXT-FORMAT.md

@@ -0,0 +1,60 @@
+# CONTEXT.md Format
+
+## Structure
+
+```md
+# {Context Name}
+
+{One or two sentence description of what this context is and why it exists.}
+
+## Language
+
+**Order**:
+{A one or two sentence description of the term}
+_Avoid_: Purchase, transaction
+
+**Invoice**:
+A request for payment sent to a customer after delivery.
+_Avoid_: Bill, payment request
+
+**Customer**:
+A person or organization that places orders.
+_Avoid_: Client, buyer, account
+```
+
+## Rules
+
+- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`.
+- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does.
+- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
+- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
+
+## Single vs multi-context repos
+
+**Single context (most repos):** One `CONTEXT.md` at the repo root.
+
+**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
+
+```md
+# Context Map
+
+## Contexts
+
+- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
+- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
+- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping
+
+## Relationships
+
+- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
+- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
+- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
+```
+
+The skill infers which structure applies:
+
+- If `CONTEXT-MAP.md` exists, read it to find contexts
+- If only a root `CONTEXT.md` exists, single context
+- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
+
+When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.

+ 74 - 0
.claude/skills/domain-modeling/SKILL.md

@@ -0,0 +1,74 @@
+---
+name: domain-modeling
+description: Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a CONTEXT.md, or recording or editing an ADR.
+---
+
+# Domain Modeling
+
+Actively build and sharpen the project's domain model as you design. This is the *active* discipline — challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely *reading* `CONTEXT.md` for vocabulary is not this skill — that's a one-line habit any skill can do. This skill is for when you're changing the model, not just consuming it.)
+
+## File structure
+
+Most repos have a single context:
+
+```
+/
+├── CONTEXT.md
+├── docs/
+│   └── adr/
+│       ├── 0001-event-sourced-orders.md
+│       └── 0002-postgres-for-write-model.md
+└── src/
+```
+
+If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:
+
+```
+/
+├── CONTEXT-MAP.md
+├── docs/
+│   └── adr/                          ← system-wide decisions
+├── src/
+│   ├── ordering/
+│   │   ├── CONTEXT.md
+│   │   └── docs/adr/                 ← context-specific decisions
+│   └── billing/
+│       ├── CONTEXT.md
+│       └── docs/adr/
+```
+
+Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.
+
+## During the session
+
+### Challenge against the glossary
+
+When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"
+
+### Sharpen fuzzy language
+
+When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things."
+
+### Discuss concrete scenarios
+
+When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.
+
+### Cross-reference with code
+
+When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"
+
+### Update CONTEXT.md inline
+
+When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).
+
+`CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.
+
+### Offer ADRs sparingly
+
+Only offer to create an ADR when all three are true:
+
+1. **Hard to reverse** — the cost of changing your mind later is meaningful
+2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
+3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
+
+If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).

+ 3 - 0
.claude/skills/domain-modeling/agents/openai.yaml

@@ -0,0 +1,3 @@
+interface:
+  display_name: "Domain Modeling"
+  short_description: "Build and sharpen a domain model"

+ 70 - 0
.claude/skills/executing-plans/SKILL.md

@@ -0,0 +1,70 @@
+---
+name: executing-plans
+description: Use when you have a written implementation plan to execute in a separate session with review checkpoints
+---
+
+# Executing Plans
+
+## Overview
+
+Load plan, review critically, execute all tasks, report when complete.
+
+**Announce at start:** "I'm using the executing-plans skill to implement this plan."
+
+**Note:** Tell your human partner that Superpowers works much better with access to subagents. The quality of its work will be significantly higher if run on a platform with subagent support (such as Claude Code or Codex). If subagents are available, use superpowers:subagent-driven-development instead of this skill.
+
+## The Process
+
+### Step 1: Load and Review Plan
+1. Read plan file
+2. Review critically - identify any questions or concerns about the plan
+3. If concerns: Raise them with your human partner before starting
+4. If no concerns: Create TodoWrite and proceed
+
+### Step 2: Execute Tasks
+
+For each task:
+1. Mark as in_progress
+2. Follow each step exactly (plan has bite-sized steps)
+3. Run verifications as specified
+4. Mark as completed
+
+### Step 3: Complete Development
+
+After all tasks complete and verified:
+- Announce: "I'm using the finishing-a-development-branch skill to complete this work."
+- **REQUIRED SUB-SKILL:** Use superpowers:finishing-a-development-branch
+- Follow that skill to verify tests, present options, execute choice
+
+## When to Stop and Ask for Help
+
+**STOP executing immediately when:**
+- Hit a blocker (missing dependency, test fails, instruction unclear)
+- Plan has critical gaps preventing starting
+- You don't understand an instruction
+- Verification fails repeatedly
+
+**Ask for clarification rather than guessing.**
+
+## When to Revisit Earlier Steps
+
+**Return to Review (Step 1) when:**
+- Partner updates the plan based on your feedback
+- Fundamental approach needs rethinking
+
+**Don't force through blockers** - stop and ask.
+
+## Remember
+- Review plan critically first
+- Follow plan steps exactly
+- Don't skip verifications
+- Reference skills when plan says to
+- Stop when blocked, don't guess
+- Never start implementation on main/master branch without explicit user consent
+
+## Integration
+
+**Required workflow skills:**
+- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
+- **superpowers:writing-plans** - Creates the plan this skill executes
+- **superpowers:finishing-a-development-branch** - Complete development after all tasks

+ 200 - 0
.claude/skills/finishing-a-development-branch/SKILL.md

@@ -0,0 +1,200 @@
+---
+name: finishing-a-development-branch
+description: Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
+---
+
+# Finishing a Development Branch
+
+## Overview
+
+Guide completion of development work by presenting clear options and handling chosen workflow.
+
+**Core principle:** Verify tests → Present options → Execute choice → Clean up.
+
+**Announce at start:** "I'm using the finishing-a-development-branch skill to complete this work."
+
+## The Process
+
+### Step 1: Verify Tests
+
+**Before presenting options, verify tests pass:**
+
+```bash
+# Run project's test suite
+npm test / cargo test / pytest / go test ./...
+```
+
+**If tests fail:**
+```
+Tests failing (<N> failures). Must fix before completing:
+
+[Show failures]
+
+Cannot proceed with merge/PR until tests pass.
+```
+
+Stop. Don't proceed to Step 2.
+
+**If tests pass:** Continue to Step 2.
+
+### Step 2: Determine Base Branch
+
+```bash
+# Try common base branches
+git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null
+```
+
+Or ask: "This branch split from main - is that correct?"
+
+### Step 3: Present Options
+
+Present exactly these 4 options:
+
+```
+Implementation complete. What would you like to do?
+
+1. Merge back to <base-branch> locally
+2. Push and create a Pull Request
+3. Keep the branch as-is (I'll handle it later)
+4. Discard this work
+
+Which option?
+```
+
+**Don't add explanation** - keep options concise.
+
+### Step 4: Execute Choice
+
+#### Option 1: Merge Locally
+
+```bash
+# Switch to base branch
+git checkout <base-branch>
+
+# Pull latest
+git pull
+
+# Merge feature branch
+git merge <feature-branch>
+
+# Verify tests on merged result
+<test command>
+
+# If tests pass
+git branch -d <feature-branch>
+```
+
+Then: Cleanup worktree (Step 5)
+
+#### Option 2: Push and Create PR
+
+```bash
+# Push branch
+git push -u origin <feature-branch>
+
+# Create PR
+gh pr create --title "<title>" --body "$(cat <<'EOF'
+## Summary
+<2-3 bullets of what changed>
+
+## Test Plan
+- [ ] <verification steps>
+EOF
+)"
+```
+
+Then: Cleanup worktree (Step 5)
+
+#### Option 3: Keep As-Is
+
+Report: "Keeping branch <name>. Worktree preserved at <path>."
+
+**Don't cleanup worktree.**
+
+#### Option 4: Discard
+
+**Confirm first:**
+```
+This will permanently delete:
+- Branch <name>
+- All commits: <commit-list>
+- Worktree at <path>
+
+Type 'discard' to confirm.
+```
+
+Wait for exact confirmation.
+
+If confirmed:
+```bash
+git checkout <base-branch>
+git branch -D <feature-branch>
+```
+
+Then: Cleanup worktree (Step 5)
+
+### Step 5: Cleanup Worktree
+
+**For Options 1, 2, 4:**
+
+Check if in worktree:
+```bash
+git worktree list | grep $(git branch --show-current)
+```
+
+If yes:
+```bash
+git worktree remove <worktree-path>
+```
+
+**For Option 3:** Keep worktree.
+
+## Quick Reference
+
+| Option | Merge | Push | Keep Worktree | Cleanup Branch |
+|--------|-------|------|---------------|----------------|
+| 1. Merge locally | ✓ | - | - | ✓ |
+| 2. Create PR | - | ✓ | ✓ | - |
+| 3. Keep as-is | - | - | ✓ | - |
+| 4. Discard | - | - | - | ✓ (force) |
+
+## Common Mistakes
+
+**Skipping test verification**
+- **Problem:** Merge broken code, create failing PR
+- **Fix:** Always verify tests before offering options
+
+**Open-ended questions**
+- **Problem:** "What should I do next?" → ambiguous
+- **Fix:** Present exactly 4 structured options
+
+**Automatic worktree cleanup**
+- **Problem:** Remove worktree when might need it (Option 2, 3)
+- **Fix:** Only cleanup for Options 1 and 4
+
+**No confirmation for discard**
+- **Problem:** Accidentally delete work
+- **Fix:** Require typed "discard" confirmation
+
+## Red Flags
+
+**Never:**
+- Proceed with failing tests
+- Merge without verifying tests on result
+- Delete work without confirmation
+- Force-push without explicit request
+
+**Always:**
+- Verify tests before offering options
+- Present exactly 4 options
+- Get typed confirmation for Option 4
+- Clean up worktree for Options 1 & 4 only
+
+## Integration
+
+**Called by:**
+- **subagent-driven-development** (Step 7) - After all tasks complete
+- **executing-plans** (Step 5) - After all batches complete
+
+**Pairs with:**
+- **using-git-worktrees** - Cleans up worktree created by that skill

+ 95 - 0
.claude/skills/git-guardrails-claude-code/SKILL.md

@@ -0,0 +1,95 @@
+---
+name: git-guardrails-claude-code
+description: Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
+---
+
+# Setup Git Guardrails
+
+Sets up a PreToolUse hook that intercepts and blocks dangerous git commands before Claude executes them.
+
+## What Gets Blocked
+
+- `git push` (all variants including `--force`)
+- `git reset --hard`
+- `git clean -f` / `git clean -fd`
+- `git branch -D`
+- `git checkout .` / `git restore .`
+
+When blocked, Claude sees a message telling it that it does not have authority to access these commands.
+
+## Steps
+
+### 1. Ask scope
+
+Ask the user: install for **this project only** (`.claude/settings.json`) or **all projects** (`~/.claude/settings.json`)?
+
+### 2. Copy the hook script
+
+The bundled script is at: [scripts/block-dangerous-git.sh](scripts/block-dangerous-git.sh)
+
+Copy it to the target location based on scope:
+
+- **Project**: `.claude/hooks/block-dangerous-git.sh`
+- **Global**: `~/.claude/hooks/block-dangerous-git.sh`
+
+Make it executable with `chmod +x`.
+
+### 3. Add hook to settings
+
+Add to the appropriate settings file:
+
+**Project** (`.claude/settings.json`):
+
+```json
+{
+  "hooks": {
+    "PreToolUse": [
+      {
+        "matcher": "Bash",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-git.sh"
+          }
+        ]
+      }
+    ]
+  }
+}
+```
+
+**Global** (`~/.claude/settings.json`):
+
+```json
+{
+  "hooks": {
+    "PreToolUse": [
+      {
+        "matcher": "Bash",
+        "hooks": [
+          {
+            "type": "command",
+            "command": "~/.claude/hooks/block-dangerous-git.sh"
+          }
+        ]
+      }
+    ]
+  }
+}
+```
+
+If the settings file already exists, merge the hook into existing `hooks.PreToolUse` array — don't overwrite other settings.
+
+### 4. Ask about customization
+
+Ask if user wants to add or remove any patterns from the blocked list. Edit the copied script accordingly.
+
+### 5. Verify
+
+Run a quick test:
+
+```bash
+echo '{"tool_input":{"command":"git push origin main"}}' | <path-to-script>
+```
+
+Should exit with code 2 and print a BLOCKED message to stderr.

+ 3 - 0
.claude/skills/git-guardrails-claude-code/agents/openai.yaml

@@ -0,0 +1,3 @@
+interface:
+  display_name: "Git Guardrails for Claude Code"
+  short_description: "Block dangerous git commands"

+ 25 - 0
.claude/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh

@@ -0,0 +1,25 @@
+#!/bin/bash
+
+INPUT=$(cat)
+COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
+
+DANGEROUS_PATTERNS=(
+  "git push"
+  "git reset --hard"
+  "git clean -fd"
+  "git clean -f"
+  "git branch -D"
+  "git checkout \."
+  "git restore \."
+  "push --force"
+  "reset --hard"
+)
+
+for pattern in "${DANGEROUS_PATTERNS[@]}"; do
+  if echo "$COMMAND" | grep -qE "$pattern"; then
+    echo "BLOCKED: '$COMMAND' matches dangerous pattern '$pattern'. The user has prevented you from doing this." >&2
+    exit 2
+  fi
+done
+
+exit 0

+ 7 - 0
.claude/skills/grill-me/SKILL.md

@@ -0,0 +1,7 @@
+---
+name: grill-me
+description: A relentless interview to sharpen a plan or design.
+disable-model-invocation: true
+---
+
+Call the Skill tool with "grilling".

+ 5 - 0
.claude/skills/grill-me/agents/openai.yaml

@@ -0,0 +1,5 @@
+interface:
+  display_name: "Grill Me"
+  short_description: "Sharpen a plan through interview"
+policy:
+  allow_implicit_invocation: false

+ 7 - 0
.claude/skills/grill-with-docs/SKILL.md

@@ -0,0 +1,7 @@
+---
+name: grill-with-docs
+description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
+disable-model-invocation: true
+---
+
+Call the Skill tool twice, for "grilling" and "domain-modeling".

+ 5 - 0
.claude/skills/grill-with-docs/agents/openai.yaml

@@ -0,0 +1,5 @@
+interface:
+  display_name: "Grill with Docs"
+  short_description: "Grill a design and write its docs"
+policy:
+  allow_implicit_invocation: false

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است