From 21f667105dfd2c619d0be417f97ecd1b1148f114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B0=E6=B6=9B?= <550947002@qq.com> Date: Thu, 3 Sep 2026 20:33:17 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9A=20docs(kb):=20=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=20matcher=204=20=E5=AD=97=E6=AE=B5(includeAny/includeAll/exclu?= =?UTF-8?q?deAny/excludeAll)=E5=AE=8C=E6=95=B4=E8=AF=AD=E4=B9=89=E4=B8=8E?= =?UTF-8?q?=E5=A1=AB=E5=85=85=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agent 侧规则(三端 + Core templates zh/en):f2s-flow2spec-unified-entry 的匹配口径从「只读 includeAny」扩为 4 字段;f2s-topic-authoring 新增 §3「matcher 分片字段语义」写作准则(否决门恒胜、AND/排除词用法、阈值提示)。 - 知识库:kb-routing-summary topic 补「matcher 分片字段语义」+「4 字段填充机制」两节(revision → 2);Core templates 双语新建 kb-routing-summary topic;index.md 3 处 includeAny 单字段口径改为 4 字段;m-kb-routing-summary matcher 补 9 个新召回词。 - 同步 npm run sync:agents 分发的三端配置根(hooks/rules/skills 镜像更新)。 - 追认 PR #55 让 routing.match() 生效的 4 字段能力,Agent 侧与公共 API 侧从此对齐。相关:PR #55、Issue #54、Issue #56(后续半自动填充备忘)。 --- .Knowledge/index.md | 8 +- .Knowledge/manifest-routing.json | 6 +- .Knowledge/matchers/m-kb-routing-summary.json | 9 + .Knowledge/template/index.template.md | 6 +- .Knowledge/topics/kb-routing-summary.md | 27 +- .claude/hooks/f2s-update-check.js | 257 ++++++++---------- .claude/rules/f2s-flow2spec-unified-entry.md | 15 +- .claude/rules/f2s-kb-feedback-closing.md | 6 + .claude/rules/f2s-topic-authoring.md | 54 +++- .claude/skills/f2s-kb-addRules/SKILL.md | 2 +- .claude/skills/f2s-kb-build/SKILL.md | 3 +- .claude/skills/f2s-kb-distill/SKILL.md | 6 +- .claude/skills/f2s-kb-sync/SKILL.md | 2 +- .claude/skills/f2s-kb-upgrade/SKILL.md | 81 +++--- .codex/hooks/f2s-update-check.js | 257 ++++++++---------- .codex/skills/f2s-kb-addRules/SKILL.md | 2 +- .codex/skills/f2s-kb-build/SKILL.md | 3 +- .codex/skills/f2s-kb-distill/SKILL.md | 6 +- .codex/skills/f2s-kb-sync/SKILL.md | 2 +- .codex/skills/f2s-kb-upgrade/SKILL.md | 81 +++--- .codex/topics/f2s-flow2spec-unified-entry.md | 15 +- .codex/topics/f2s-kb-feedback-closing.md | 6 + .codex/topics/f2s-topic-authoring.md | 54 +++- .cursor/hooks/f2s-update-check.js | 257 ++++++++---------- .cursor/rules/f2s-flow2spec-unified-entry.mdc | 15 +- .cursor/rules/f2s-kb-feedback-closing.mdc | 6 + .cursor/rules/f2s-topic-authoring.mdc | 54 +++- .cursor/skills/f2s-kb-addRules/SKILL.md | 2 +- .cursor/skills/f2s-kb-build/SKILL.md | 3 +- .cursor/skills/f2s-kb-distill/SKILL.md | 6 +- .cursor/skills/f2s-kb-sync/SKILL.md | 2 +- .cursor/skills/f2s-kb-upgrade/SKILL.md | 81 +++--- .../core/templates/en-US/knowledge/index.md | 6 +- .../knowledge/topics/kb-routing-summary.md | 64 +++++ .../rules/f2s-flow2spec-unified-entry.md | 6 +- .../en-US/rules/f2s-topic-authoring.md | 45 ++- .../core/templates/zh-CN/knowledge/index.md | 6 +- .../knowledge/topics/kb-routing-summary.md | 64 +++++ .../rules/f2s-flow2spec-unified-entry.md | 6 +- .../zh-CN/rules/f2s-topic-authoring.md | 45 ++- 40 files changed, 907 insertions(+), 669 deletions(-) create mode 100644 packages/core/templates/en-US/knowledge/topics/kb-routing-summary.md create mode 100644 packages/core/templates/zh-CN/knowledge/topics/kb-routing-summary.md diff --git a/.Knowledge/index.md b/.Knowledge/index.md index 57c1edd..03de2d1 100644 --- a/.Knowledge/index.md +++ b/.Knowledge/index.md @@ -10,7 +10,7 @@ ## 推荐阅读顺序 1. `.Knowledge/manifest-routing.json`(任务路由、`topicPaths`、`topicDependencies`、`fallbackTopic`) -2. 按需:由 `matcherPath` 读取 `.Knowledge/matchers/.json`(`includeAny` 关键词) +2. 按需:由 `matcherPath` 读取 `.Knowledge/matchers/.json`(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词) 3. 按需:本 `index.md`(主题语义与边界) 4. `.Knowledge/topics/.md`(执行约束与流程) 5. 按需:`.Knowledge/stock-docs/`、`.Knowledge/req-docs/` @@ -41,7 +41,7 @@ | flow2spec-dsh-adapter | `.Knowledge/topics/flow2spec-dsh-adapter.md` | `flow2spec init dsh`、DeepSeek Harness 项目技能发现与 `.dsh/` 目录适配 | 用户文档:`docs/使用说明.md` / `docs/en/usage-guide.md`;实现:`lib/dshAgentsAdapter.js` | | flow2spec-core-package | `.Knowledge/topics/flow2spec-core-package.md` | `@double-coding/flow2spec-core` 职责边界:核心实现、发包模板真源、原生插件 API 与 CLI/legacy shim 消费关系 | 开发者文档:`packages/core/README.md`;入口:`packages/core/index.js` | | flow2spec-qoder-plugin | `.Knowledge/topics/flow2spec-qoder-plugin.md` | Qoder 插件构建、`build:qoder-plugin`、zip 打包与插件市场分发 | 构建脚本:`scripts/build-qoder-plugin.js`;内容源:`packages/core/index.js`(resources API) | -| kb-routing-summary | `.Knowledge/topics/kb-routing-summary.md` | 路由初筛 `taskToTopicRules[].summary` 字段:frontmatter 唯一手写源、kb build 机械同步、kb check 质量校验与存量修复 | 引擎:`packages/core/lib/knowledgeEngine.js`;创作规范:配置根 `rules/f2s-topic-authoring.*` | +| kb-routing-summary | `.Knowledge/topics/kb-routing-summary.md` | 路由初筛 `taskToTopicRules[].summary` 字段与 matcher 4 字段(`includeAny`/`includeAll`/`excludeAny`/`excludeAll`)语义 | 引擎:`packages/core/lib/knowledgeEngine.js`、`packages/core/lib/routing.js`;创作规范:配置根 `rules/f2s-topic-authoring.*` | 每主题保留 **1–3 条** 可点击摘要链接;全量路径对照写入 `.Knowledge/migration-report.md`(迁移场景)。 其中 **`implement-tech-design`**、**`f2s-doc-routing`**、**`config-precheck`**、**`f2s-task`** 在 `topics/` 内为**路由摘要**;执行长文见配置根 **`rules/f2s-*.md(c)`**;使用 Codex 时见 **`.codex/AGENTS.md`**、**`.codex/topics/f2s-*.md`**(`f2s-config-check` 与 `AGENTS` 前置同源,按需打开)。**`f2s-knowledge-preflight`** 与 **`f2s-kb-feedback-closing`** 是普通问答首读 / 源码补答收口门禁,作为配置根规则 / Codex 专题长文生效,不写入 `topicPaths` 或 `taskToTopicRules`;**`f2s-kb-feedback-closing`** 在 cases 1–3 建议执行 **`f2s-kb-distill`**。 @@ -50,7 +50,7 @@ ## 命中与执行(与统一入口一致) -- **路由**:`taskToTopicRules` 给出任务 → 主题集合;**关键词**在 matcher 分片的 `includeAny`。 +- **路由**:`taskToTopicRules` 给出任务 → 主题集合;**关键词**在 matcher 分片(`includeAny` / `includeAll` 资格门 + `excludeAny` / `excludeAll` 否决门;否决优先于 `task` 精确命中,详见 `topics/kb-routing-summary.md`)。 - **依赖**:命中主主题前,按 `topicDependencies` 先读依赖主题。 - **兜底**:`fallbackTopic` 指向分诊主题(如 `fallback-triage`),仅低置信度上下文,**不得**当作最终命中直接改代码。 - **执行链**:`match → expand → verify → act`;`expand` 须含依赖展开,并保留次高候选做校验。 @@ -76,7 +76,7 @@ | 情况 | 你怎么做 | | --- | --- | -| 有文档但没配到(1a) | 维护侧:`f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补路由与 `includeAny`。执行侧:分诊主题澄清任务类型,**不**用全仓扫替代 manifest。 | +| 有文档但没配到(1a) | 维护侧:`f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补路由与 matcher 词表(`includeAny` / `includeAll` / `excludeAny` / `excludeAll`)。执行侧:分诊主题澄清任务类型,**不**用全仓扫替代 manifest。 | | 配到了但不够(1b) | 走依赖与次高候选 → `verify` 点名缺哪篇文档;仍缺则向用户要路径或补 `req-docs`。 | | 库里没有(2) | 承认缺口 → 代码下钻或请用户补需求/方案文档。 | | 反复读 manifest 费 token(2a) | 同一任务线内 routing 只当快照;只读命中项的单个 matcher;不遍历整个 `matchers/` 目录枚举;`index.md` 勿与 routing 循环互刷。 | diff --git a/.Knowledge/manifest-routing.json b/.Knowledge/manifest-routing.json index f5ceecb..ab16998 100644 --- a/.Knowledge/manifest-routing.json +++ b/.Knowledge/manifest-routing.json @@ -1,5 +1,5 @@ { - "version": "3.5.0", + "version": "3.6.2", "knowledgeRoot": ".Knowledge", "matcherKey": "matcherId", "sourceOfTruth": ".Knowledge/manifest-routing.json", @@ -208,11 +208,11 @@ "topics": [ "kb-routing-summary" ], - "summary": "初筛召回 summary 字段的同步、校验与创作规范" + "summary": "初筛 summary 与 matcher 分片 4 字段(资格/否决门)语义" } ], "projectRev": 2, - "pkgRev": 2, + "pkgRev": 3, "topicMetadata": { "implement-tech-design": { "primary": "policy", diff --git a/.Knowledge/matchers/m-kb-routing-summary.json b/.Knowledge/matchers/m-kb-routing-summary.json index 84772a9..79f7f38 100644 --- a/.Knowledge/matchers/m-kb-routing-summary.json +++ b/.Knowledge/matchers/m-kb-routing-summary.json @@ -11,6 +11,15 @@ "路由 miss", "matcher", "includeAny", + "includeAll", + "excludeAny", + "excludeAll", + "matcher 字段", + "matcher 4 字段", + "否决门", + "资格门", + "路由匹配", + "路由字段", "语义锚" ] } diff --git a/.Knowledge/template/index.template.md b/.Knowledge/template/index.template.md index bce6b9d..9e7c519 100644 --- a/.Knowledge/template/index.template.md +++ b/.Knowledge/template/index.template.md @@ -10,7 +10,7 @@ ## 推荐阅读顺序 1. `.Knowledge/manifest-routing.json`(任务路由、`topicPaths`、`topicDependencies`、`fallbackTopic`) -2. 按需:由 `matcherPath` 读取 `.Knowledge/matchers/.json`(`includeAny` 关键词) +2. 按需:由 `matcherPath` 读取 `.Knowledge/matchers/.json`(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词) 3. 按需:本 `index.md`(主题语义与边界) 4. `.Knowledge/topics/.md`(执行约束与流程) 5. 按需:`.Knowledge/stock-docs/`、`.Knowledge/req-docs/` @@ -37,7 +37,7 @@ ## 命中与执行(与统一入口一致) -- **路由**:`taskToTopicRules` 给出任务 → 主题集合;**关键词**在 matcher 分片的 `includeAny`。 +- **路由**:`taskToTopicRules` 给出任务 → 主题集合;**关键词**在 matcher 分片(`includeAny` / `includeAll` 资格门 + `excludeAny` / `excludeAll` 否决门;否决优先于 `task` 精确命中)。 - **依赖**:命中主主题前,按 `topicDependencies` 先读依赖主题。 - **兜底**:`fallbackTopic` 指向分诊主题(如 `fallback-triage`),仅低置信度上下文,**不得**当作最终命中直接改代码。 - **执行链**:`match → expand → verify → act`;`expand` 须含依赖展开,并保留次高候选做校验。 @@ -63,7 +63,7 @@ | 情况 | 你怎么做 | | --- | --- | -| 有文档但没配到(1a) | 维护侧:`f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补路由与 `includeAny`。执行侧:分诊主题澄清任务类型,**不**用全仓扫替代 manifest。 | +| 有文档但没配到(1a) | 维护侧:`f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补路由与 matcher 词表(`includeAny` / `includeAll` / `excludeAny` / `excludeAll`)。执行侧:分诊主题澄清任务类型,**不**用全仓扫替代 manifest。 | | 配到了但不够(1b) | 走依赖与次高候选 → `verify` 点名缺哪篇文档;仍缺则向用户要路径或补 `req-docs`。 | | 库里没有(2) | 承认缺口 → 代码下钻或请用户补需求/方案文档。 | | 反复读 manifest 费 token(2a) | 同一任务线内 routing 只当快照;只读命中项的单个 matcher;不遍历整个 `matchers/` 目录枚举;`index.md` 勿与 routing 循环互刷。 | diff --git a/.Knowledge/topics/kb-routing-summary.md b/.Knowledge/topics/kb-routing-summary.md index 93e24d1..1b38967 100644 --- a/.Knowledge/topics/kb-routing-summary.md +++ b/.Knowledge/topics/kb-routing-summary.md @@ -1,7 +1,7 @@ --- id: kb-routing-summary -revision: 0 -summary: "初筛召回 summary 字段的同步、校验与创作规范" +revision: 2 +summary: "初筛 summary 与 matcher 分片 4 字段(资格/否决门)语义" primary: feature confidence: manual --- @@ -29,6 +29,29 @@ confidence: manual - 存量修复走 `f2s-kb-upgrade` 完整流程步骤 3a.7 / 3a.8:`kb build --fix-topics` 补占位头部 → `kb check --strict` 报 summary warning → agent 逐个 Read 正文补写语义摘要 → `kb build` 同步进 `rule.summary`。 - `f2s-kb-upgrade` 步骤 -1 的 A 分支(包侧均为最新)**不得直接停止**:须先做项目侧对齐检查(manifest `version`/`pkgRev` 对比包 Template Version + `kb check --strict`),未对齐视同 Template 更新转完整流程;否则刚升完包的老项目会被误判「无事可做」跳过 summary 补写。 +## matcher 分片字段语义 + +matcher 分片(`.Knowledge/matchers/.json`)有 4 个字段参与路由匹配,按「否决门(恒胜) → 资格门(OR ∪ AND)」两级判定: + +- **资格门**(至少满足其一才进入候选): + - `includeAny`:任一短语命中(OR)——最常用 + - `includeAll`:所有短语全部命中(AND)——表达组合词 +- **否决门**(任一门命中则整条规则出局,**优先于 `task` 精确命中**): + - `excludeAny`:任一短语命中(OR) + - `excludeAll`:所有短语全部命中(AND)——半命中不否决 + +否决优先于 `task` 精确命中:排除词表达「此请求不属于该任务域」,语义上比精确命中更强。存量分片(仅有 `includeAny`)行为不变。 + +字段写作规范(何时用 AND / 什么时候上排除词 / 与 12 词阈值的关系)见 `rules/f2s-topic-authoring` 「matcher 分片字段语义」一节。实现:`packages/core/lib/routing.js:match()`;契约测试:`scripts/test-routing-semantics.js`。 + +### 4 字段填充机制(按需人写,引擎不生成) + +- `includeAll` / `excludeAny` / `excludeAll` **无自动填充**——引擎只做透传:`normalizeDeltaMatcher` 在 `kb apply` 写盘时保留 delta 已带字段,`kb build` 不动 matcher 分片,`flow2spec init` 分发的默认分片模板也只有 `includeAny`。 +- 赋值的**唯一路径**:用户遇到误路由 / AND 组合词需求 → Agent 依 `f2s-topic-authoring` §3 写作准则手写进对应 `matchers/.json`(或用户直接手工编辑)。 +- **无 skill 会主动生成这 3 个字段**——即便用户描述含"排除/避免命中"语义,也需 Agent 观察 + 判断 + 手写,不是自动流程。 +- 定位:**逃生舱,不是默认装备**——大部分主题只用 `includeAny` 就够,git 全历史至本次同步前,生产分片对这 3 字段的赋值次数为 **0**。 +- 后续演进见 [issue #56](https://github.com/double-coding-lab/Flow2Spec/issues/56)(半自动填充能力备忘)。 + ## 边界 - `task` 字段保持稳定 id 语义(kb 引擎按 `task` 合并规则),不承载召回语义。 diff --git a/.claude/hooks/f2s-update-check.js b/.claude/hooks/f2s-update-check.js index ad5436f..208c7b8 100644 --- a/.claude/hooks/f2s-update-check.js +++ b/.claude/hooks/f2s-update-check.js @@ -1,189 +1,156 @@ #!/usr/bin/env node 'use strict'; -/** - * flow2spec SessionStart hook — 每天第一次对话时检查版本更新。 - * 比较本地知识库 manifest-routing.json 的 version 与 npm 最新版本: - * - 一致或本地更新 → 静默退出 - * - 落后 → 向 Agent 上下文注入提示(agent 可直接代跑 flow2spec init;主题层变更再走 f2s-kb-upgrade) - * 已检查过且无需升级则静默;已检查且仍需升级时,每个新会话继续注入提醒。 - * 由 flow2spec init 写入对应 agent 的 hooks/f2s-update-check.js。 - */ -const fs = require('fs'); + +const fs = require('fs'); const path = require('path'); const { execFileSync } = require('child_process'); const MANIFEST_PATH = path.join(process.cwd(), '.Knowledge', 'manifest-routing.json'); -const CACHE_DIR = path.join(process.cwd(), '.Knowledge'); -const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json'); +const CACHE_FILE = path.join(process.cwd(), '.Knowledge', 'update-check.json'); const PACKAGE_NAME_PLACEHOLDER = '__FLOW2SPEC_' + 'PACKAGE_NAME__'; const PACKAGE_NAME = '@double-coding/flow2spec-core'; +const GENERATED_CORE_VERSION = '3.7.2'; +const GENERATED_TEMPLATE_VERSION = '3.6.2'; -// ── 缓存 ──────────────────────────────────────────────────────────────────── - -function readCache() { - if (!fs.existsSync(CACHE_FILE)) return null; - try { - const d = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); - if (!d || typeof d !== 'object') return null; - const checkedAt = Number(d.checkedAt || 0); - if (!checkedAt) return null; - if (new Date(checkedAt).toDateString() !== new Date().toDateString()) return null; - return d; - } catch (_) { return null; } +function readJson(file) { + try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { return null; } } -function getProjectName() { - try { - const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); - if (pkg && pkg.name) return String(pkg.name); - } catch (_) {} - return path.basename(process.cwd()); +function parseVer(value) { + return String(value || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((part) => { + const number = Number.parseInt(part, 10); + return Number.isFinite(number) ? number : 0; + }); } -function buildNotice(latestNpm, manifestVersion) { - const userMsg = `📦 [flow2spec] 当前项目「${getProjectName()}」知识库版本 v${manifestVersion},低于最新包版本 v${latestNpm}。我可以直接代跑 flow2spec init 完成更新;若本次更新包含主题层变更,再走 f2s-kb-upgrade 完整流程。`; - return [ - `[flow2spec/agent-instruction] 你必须在本轮回复开头将下列升级提示原文转告用户,不得省略、不得仅在内部判断后跳过:`, - ``, - userMsg, - ``, - `[flow2spec/agent-instruction] 转告后可直接替用户执行 \`flow2spec init <已初始化的 agents,如 claude cursor codex>\` 完成更新,无需用户手动执行 f2s-kb-upgrade。init 成功后读取 .Knowledge/manifest-routing.json:若 projectRev 与 pkgRev 相等(非主题版本更新),更新已完成,删除 .Knowledge/update-check.json;若两者不等(包含主题层变更),继续执行 f2s-kb-upgrade skill 完整流程(可从其步骤 2c 起,不必重复 init)。`, - ].join('\n'); +function cmpVer(left, right) { + const a = parseVer(left), b = parseVer(right); + for (let index = 0; index < 3; index += 1) { + const difference = (a[index] || 0) - (b[index] || 0); + if (difference !== 0) return difference; + } + return 0; } -function emitNotice(notice) { - process.stdout.write( - JSON.stringify({ - additional_context: notice, - hookSpecificOutput: { - hookEventName: 'SessionStart', - additionalContext: notice, - }, - }) + '\n' - ); +function getProjectName() { + return readJson(path.join(process.cwd(), 'package.json'))?.name || path.basename(process.cwd()); } -function writeCache(latestNpm, manifestVersion) { - try { - const needsUpgrade = cmpVer(manifestVersion, latestNpm) < 0; - fs.writeFileSync( - CACHE_FILE, - `${JSON.stringify({ - latestNpm, - manifestVersion, - needsUpgrade, - notice: needsUpgrade ? buildNotice(latestNpm, manifestVersion) : '', - checkedAt: Date.now(), - }, null, 2)}\n`, - 'utf8' - ); - } catch (_) {} +function getManifestVersion() { + return readJson(MANIFEST_PATH)?.version || null; } -function deleteCache() { - try { - if (fs.existsSync(CACHE_FILE)) fs.unlinkSync(CACHE_FILE); - } catch (_) {} +function getPackageName() { + return PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER + ? PACKAGE_NAME + : '@double-coding/flow2spec-core'; } -// ── 版本比较 ───────────────────────────────────────────────────────────────── - -function parseVer(v) { - return String(v || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((p) => { - const n = Number.parseInt(p, 10); - return Number.isFinite(n) ? n : 0; - }); +function isEnabled() { + const config = readJson(path.join(process.cwd(), 'flow2spec.config.json')); + return config?.updateCheck?.enabled !== false; } -/** a < b → 负数;a === b → 0;a > b → 正数 */ -function cmpVer(a, b) { - const av = parseVer(a), bv = parseVer(b); - for (let i = 0; i < 3; i++) { - const d = (av[i] || 0) - (bv[i] || 0); - if (d !== 0) return d; - } - return 0; +function readCache() { + const cache = readJson(CACHE_FILE); + if (!cache?.checkedAt) return null; + return new Date(cache.checkedAt).toDateString() === new Date().toDateString() ? cache : null; } -// ── 读取 ───────────────────────────────────────────────────────────────────── +function queryLatestMetadata(packageName) { + const output = execFileSync( + 'npm', + ['view', packageName, 'version', 'templateVersion', '--json', '--registry=https://registry.npmjs.org'], + { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }, + ); + const metadata = JSON.parse(output); + const latestCoreVersion = typeof metadata === 'string' ? metadata : metadata.version; + const latestTemplateVersion = typeof metadata === 'string' + ? metadata + : metadata.templateVersion || metadata.version; + return { latestCoreVersion, latestTemplateVersion }; +} -function getManifestVersion() { - if (!fs.existsSync(MANIFEST_PATH)) return null; - try { - return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8')).version || null; - } catch (_) { return null; } +function updateState(metadata, manifestVersion) { + const coreUpdateAvailable = cmpVer(GENERATED_CORE_VERSION, metadata.latestCoreVersion) < 0; + const templateUpdateAvailable = cmpVer(manifestVersion, metadata.latestTemplateVersion) < 0; + return { + packageName: getPackageName(), + generatedByCoreVersion: GENERATED_CORE_VERSION, + generatedTemplateVersion: GENERATED_TEMPLATE_VERSION, + manifestVersion, + ...metadata, + coreUpdateAvailable, + templateUpdateAvailable, + needsUpgrade: coreUpdateAvailable || templateUpdateAvailable, + }; } -function getPackageName() { - if (PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER) { - return PACKAGE_NAME; +function buildNotice(state) { + const project = getProjectName(); + const versionSummary = `Core v${state.generatedByCoreVersion} -> v${state.latestCoreVersion},Template v${state.manifestVersion} -> v${state.latestTemplateVersion}`; + if (state.templateUpdateAvailable) { + return [ + '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', + '', + `📦 [flow2spec] 当前项目「${project}」有模板更新(${versionSummary})。`, + '', + '[flow2spec/agent-instruction] 可先执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新),再执行 `flow2spec init <已初始化的 agents>`。init 后读取 `.Knowledge/manifest-routing.json`:projectRev 与 pkgRev 相等则更新完成并删除 `.Knowledge/update-check.json`;不等时再执行 f2s-kb-upgrade(可从步骤 2c 继续)。', + ].join('\n'); } - return '@double-coding/flow2spec'; + return [ + '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', + '', + `📦 [flow2spec] 当前项目「${project}」仅有 Core 程序更新(${versionSummary}),模板版本未变化。`, + '', + '[flow2spec/agent-instruction] 可执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新),随后执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook。Template Version 未变化,不进入 f2s-kb-upgrade;完成后删除 `.Knowledge/update-check.json`。', + ].join('\n'); } -function queryNpmLatest(pkgName) { - return execFileSync('npm', ['view', pkgName, 'version'], { - encoding: 'utf8', - timeout: 5000, - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); +function emitNotice(notice) { + process.stdout.write(JSON.stringify({ + additional_context: notice, + hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: notice }, + }) + '\n'); } -// ── 配置开关 ────────────────────────────────────────────────────────────────── - -function isEnabled() { +function writeCache(state) { try { - const cfg = JSON.parse(fs.readFileSync( - path.join(process.cwd(), 'flow2spec.config.json'), 'utf8' - )); - const uc = cfg && cfg.updateCheck; - if (uc && typeof uc.enabled === 'boolean') return uc.enabled; - return true; - } catch (_) { return true; } + fs.writeFileSync(CACHE_FILE, `${JSON.stringify({ + ...state, + latestNpm: state.latestTemplateVersion, + notice: state.needsUpgrade ? buildNotice(state) : '', + checkedAt: Date.now(), + }, null, 2)}\n`, 'utf8'); + } catch (_) {} } -// ── 主流程 ──────────────────────────────────────────────────────────────────── +function deleteCache() { + try { fs.rmSync(CACHE_FILE, { force: true }); } catch (_) {} +} function main() { - if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return; - if (!isEnabled()) return; - const cache = readCache(); - if (cache) { - // 今天已检查过则不重复查 npm;若缓存显示仍需升级,每个新会话继续提醒。 - const needsUpgrade = cache.needsUpgrade === true || - cmpVer(cache.manifestVersion, cache.latestNpm) < 0; - if (needsUpgrade) { - const currentManifestVersion = getManifestVersion(); - if (currentManifestVersion && cache.latestNpm && - cmpVer(currentManifestVersion, cache.latestNpm) >= 0) { - deleteCache(); - return; - } - // SessionStart 进入新会话:缓存命中且仍需升级,直接 emit。 - const notice = buildNotice(cache.latestNpm, cache.manifestVersion); - emitNotice(notice); - } - return; - } - + if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || !isEnabled()) return; const manifestVersion = getManifestVersion(); - if (!manifestVersion) return; // 无知识库,跳过 - - let latestNpm; - try { - const pkgName = getPackageName(); - latestNpm = queryNpmLatest(pkgName); - } catch (_) { - return; // 网络不通,静默退出,不写缓存(下次还会重试) + if (!manifestVersion) return; + + const cached = readCache(); + if (cached) { + const state = updateState({ + latestCoreVersion: cached.latestCoreVersion, + latestTemplateVersion: cached.latestTemplateVersion || cached.latestNpm, + }, manifestVersion); + if (!state.needsUpgrade) deleteCache(); + else emitNotice(buildNotice(state)); + return; } - // 写缓存(无论是否需要升级,今天不再重复检查) - writeCache(latestNpm, manifestVersion); - - if (cmpVer(manifestVersion, latestNpm) >= 0) return; // 已是最新 - - const notice = buildNotice(latestNpm, manifestVersion); - emitNotice(notice); + let metadata; + try { metadata = queryLatestMetadata(getPackageName()); } catch (_) { return; } + if (!metadata.latestCoreVersion || !metadata.latestTemplateVersion) return; + const state = updateState(metadata, manifestVersion); + writeCache(state); + if (state.needsUpgrade) emitNotice(buildNotice(state)); } main(); diff --git a/.claude/rules/f2s-flow2spec-unified-entry.md b/.claude/rules/f2s-flow2spec-unified-entry.md index c22960f..23a1fa0 100644 --- a/.claude/rules/f2s-flow2spec-unified-entry.md +++ b/.claude/rules/f2s-flow2spec-unified-entry.md @@ -28,7 +28,7 @@ description: Flow2Spec 统一知识库入口,按 .Knowledge 渐进式读取 ## 读取顺序(必须) -1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;按需根据 `matcherPath` 读取 matcher 分片获取 `includeAny` 关键词;无法命中时进入补召回阶段。 +1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;初筛证据为每条规则的 `task` 名与 `summary`(一句话意图摘要);按需根据 `matcherPath` 读取 matcher 分片获取词表(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词;否决门恒胜,优先于 `task` 精确命中,详见 `topics/kb-routing-summary.md`);无法命中时进入补召回阶段。 - 若命中主题在 `topicDependencies` 中存在依赖,先读依赖主题,再读主主题。 - 路由清单仅通过 `f2s-*` 技能流程维护,不依赖额外 CLI 子命令。 2. `.Knowledge/index.md` 按需读取,仅用于确认主题语义与边界。 @@ -36,9 +36,9 @@ description: Flow2Spec 统一知识库入口,按 .Knowledge 渐进式读取 4. 若需要背景,再读 `.Knowledge/stock-docs/.md`。 5. 仅在前四步不足时下钻业务源码。 6. 命中后必须执行 `match -> expand -> verify -> act`: - - `match`:先取主候选; + - `match`:以规则的 `task` 名与 `summary` 做语义匹配取主候选;`summary` 与 `includeAny` 均为语义锚而非字面白名单,允许近义命中; - `expand`:展开 `topicDependencies`,并保留次高候选做补充校验; - - `verify`:执行前做缺口检查(关键主题/边界/上下文是否缺失); + - `verify`:执行前做缺口检查(关键主题/边界/上下文是否缺失);**若命中主题正文未覆盖用户问句的核心名词,必须并读次高候选的 `summary` 与 matcher 分片再定,不得直接作答**; - `act`:仅在置信度足够时执行;低置信度必须先澄清。 7. 仅在以下条件之一成立时,允许执行跨 matcher 全量补检索(top-k): - `taskToTopicRules` 无命中; @@ -54,11 +54,12 @@ description: Flow2Spec 统一知识库入口,按 .Knowledge 渐进式读取 ## 机读事实源口径(规则层) - `taskToTopicRules`:任务路由第一优先级。 +- `taskToTopicRules[].summary`:初筛召回字段(一句话意图摘要),由 `flow2spec kb build` 从 topic frontmatter `summary` 机械同步,不手写 manifest 侧;作语义锚参与 match 初筛。 - `taskToTopicRules[].matcherPath`:匹配词分片直链路径,按需读取单个 matcher 文件。 - `taskToTopicRules[].matcherId`:matcher 的稳定标识,需与 matcher 分片内 `id` 一致。 - `topicDependencies`:主主题命中后先加载依赖主题。 - `topicMetadata`:主题治理元数据,只影响阅读预期,不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性;执行强制性始终以 `AGENTS.md`、rules、skills 与 topic 正文中的明确要求为准。读到 `topicMetadata[topicId].primary` / `tags` 时:`config` 关注配置项、开关、默认值、初始化参数;`policy` 优先检查正文中的必须/禁止/门禁/流程约束;`feature` 作为已落地业务/产品能力背景;`module` 作为目录、包、模块边界与工程结构背景。`confidence` 仅允许 `manual` / `inferred`;无明确分类证据时不写 metadata。 -- `matcherPath(includeAny)`:任务关键词匹配词表。 +- `matcherPath(includeAny/includeAll/excludeAny/excludeAll)`:任务关键词匹配词表(前两者为资格门,后两者为否决门;详见 `topics/kb-routing-summary.md`)。 - `fallbackTopic`:任务与关键词都未命中时必须读取,但仅作低置信度兜底,不是最终执行依据。 - `.Knowledge/manifest-routing.json + matcherPath 分片文件` 是机读事实源(关键词仅在 `matchers/*.json`)。 - `.Knowledge/index.md` 不是机读事实源,仅作人读导航与语义边界校验。 @@ -68,7 +69,7 @@ description: Flow2Spec 统一知识库入口,按 .Knowledge 渐进式读取 | 情况 | 对策 | | --- | --- | -| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` 覆盖用户常用说法。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | +| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` / `includeAll` 覆盖用户常用说法,必要时用 `excludeAny` 排除误路由。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | | **1b 命中了但上下文不够** | 先 `expand`(`topicDependencies` + 次高候选),再 `verify` 点名缺哪份 `stock-docs`/`req-docs` 或哪段 topic;仍不足则 **向用户要文档或路径**,不要无门槛跨 matcher 全量补检索。**Agent 若需下钻源码**:须先对用户做**可见的缺口说明**(已读 KB、缺什么、拟读哪 1~2 个文件),见 **`f2s-knowledge-preflight`**「缺口闸门」;**禁止**无说明地连续 `Grep`/乱序探源。 | | **2 库里没有对应文档** | 一次读完 routing + 已命中 matcher + 相关 topic 后,在回复中 **明确承认知识库无覆盖**,再选:下钻业务代码 / 请用户补充 `req-docs` 或 PRD。**禁止**用反复读清单假装「再找一遍就会有」。**下钻源码前**同样须满足 **`f2s-knowledge-preflight`**「缺口闸门」的可见说明。 | | **2a 反复读清单耗 token** | **同一任务线内** `manifest-routing.json` 视为稳定快照:再次全文读取须说明理由(例如用户声明已通过 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 等更新路由或知识、或**手动编辑**了 manifest/matcher)。**勿将**仅执行 **`flow2spec init`** 等同于「业务知识库已更新」:`init` 以配置根落盘、目录补齐与包级路由结构对齐为主;**stock-docs / req-docs、topics 路由摘要、matchers 词条**由 **`f2s-*` 技能流程**维护;`init` 会把规则写入配置根 **`rules/*`**(或等价扩展名),并为 Codex 写入 **`.codex/topics/*.md`**。只读 **当前规则对应的单个** `matcherPath`;不要为枚举而遍历整个 `matchers/` 目录。`index.md` 仅在需核对主题语义时打开,禁止与 manifest 交替「刷清单」。 | @@ -96,9 +97,9 @@ description: Flow2Spec 统一知识库入口,按 .Knowledge 渐进式读取 **规则层双保险**(与脚本缓存互为备份): 1. 读 `flow2spec.config.json` → 若 `updateCheck.enabled` 不为 `true`,跳过,不做任何提示。 -2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日(`new Date(checkedAt).toDateString() === new Date().toDateString()`),不重复查 npm;但若 `needsUpgrade=true` 或 `latestNpm > manifestVersion`,本会话首次回复用户时仍须转告升级提示,并可**直接替用户执行** `flow2spec init <已初始化的 agents>` 完成更新——init 后读 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` 相等(非主题版本更新)即完成并删除该缓存;不等(含主题层变更)再执行 `f2s-kb-upgrade` 完整流程(可从其步骤 2c 起,不重复 init);若当前 `.Knowledge/manifest-routing.json.version` 已不低于 `latestNpm`,删除该缓存并不再提示。 +2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日,不重复查 npm;`coreUpdateAvailable=true` 时可执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新)。若 `templateUpdateAvailable=false`,随后只执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook,删除缓存,不进入 `f2s-kb-upgrade`;若 `templateUpdateAvailable=true`,更新后执行 init,再以 `projectRev` / `pkgRev` 判定快速路径或完整流程。`.Knowledge/manifest-routing.json.version` 表示 Template Version,禁止与 Core Version 直接比较。 3. 上述两步均未跳过时:执行当前 agent 配置根下的更新检测脚本(Claude:`node .claude/hooks/f2s-update-check.js`;Cursor:`node .cursor/hooks/f2s-update-check.js`;Codex:`node .codex/hooks/f2s-update-check.js`),解析标准输出的 JSON: - - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 执行(非主题版本更新可直接代跑 `flow2spec init`;主题层变更再执行 `f2s-kb-upgrade` skill)。 + - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 分别处理 Core-only 与 Template 更新。 - 无输出或解析失败:静默,不提示。 4. 以上步骤出现任何错误,静默跳过,不影响正常对话。 diff --git a/.claude/rules/f2s-kb-feedback-closing.md b/.claude/rules/f2s-kb-feedback-closing.md index 857f904..c9f815c 100644 --- a/.claude/rules/f2s-kb-feedback-closing.md +++ b/.claude/rules/f2s-kb-feedback-closing.md @@ -107,6 +107,12 @@ description: 普通问答读取源码后的知识库补充建议收口规则; 这样能避免"已有 topic 还建议 add"的误判。 +## 路由 miss 反哺(强制) + +- **判定**:本轮答案的核心事实由某 topic 提供,但该 topic **不是初筛主命中**(靠全量补检索、次高候选并读或用户点名才找到)→ 计为一次路由 miss。 +- **动作**:在四 case 收口之外,把本轮用户的原始问法回填该 topic 的召回锚——按 `f2s-topic-authoring`「初筛召回规范」补 frontmatter `summary` 的核心名词或 matcher `includeAny` 的单概念词,并跑 `flow2spec kb build` 同步 rule.summary。此项是直接落盘的最小修复,不依赖用户再跑 distill。 +- 即使收口走 case 4(知识库已覆盖),只要命中路径是 miss 后补救,仍须执行本反哺,并在收口块后追加一行:`已回填路由: 的 summary/includeAny 补「<词>」`。 + ## 输出格式 - case 1~3:输出一个 Markdown 引用块,依次写 `f2s-kb-distill` 命令 + 一行空行 + **本轮将入库**概要(一句话,见上文「概要要求」)。 diff --git a/.claude/rules/f2s-topic-authoring.md b/.claude/rules/f2s-topic-authoring.md index 49e4a21..5487781 100644 --- a/.claude/rules/f2s-topic-authoring.md +++ b/.claude/rules/f2s-topic-authoring.md @@ -31,13 +31,49 @@ description: Flow2Spec 主题创作准则:topic 命名 / 骨架 / topicMetadat 每个 topic 至少包含: 1. **标题与一句话意图**(一行写清"该 topic 解决什么"); -2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` 语义一致); +2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` / `includeAll` 语义一致); 3. **核心规则 / 流程**(可执行知识;步骤须可由 Agent 复现); 4. **依赖声明**(若 `topicDependencies` 中存在依赖项,正文须显式写一句「执行前须先读依赖主题 ``」,参考 `topics/f2s-req-plan.md` 首段写法); 5. **边界与禁止项**(避免膨胀到隔壁 topic); 6. **长文背景 / 详细资料引用**(如需承载业务背景):只列 `.Knowledge/stock-docs/*_终稿.md` 的可点击 Markdown 链接(1–3 条即可);**禁止**直接列 `.Knowledge/req-docs/*` 作为长文背景来源;无对应终稿时**先生成终稿**再回填此小节。 -## 3. topicMetadata 判定准则 +### frontmatter `summary` 与 `includeAny` 创作规范(初筛召回,硬约束) + +topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest-routing.taskToTopicRules[].summary`,是 Agent 初筛的常驻语义锚——写作质量直接决定该 topic 能否被自然问法命中: + +- **`summary`**:一句话写清「职责 + 用户会问的核心名词」,覆盖 topic 的全部职责域(如同时承担“职责边界”与“资料入口”,两者都要出现);软上限 30 字(英文 15 词),硬上限 40 字 / 20 词(`kb check` 校验);禁止 `(路由摘要)`、`TODO` 等占位写法。 +- **`includeAny`**:单概念核心词优先(自然问法中会独立出现的最小词,如「原型」「流程图」而非「原型位置」「业务流程图」);复合词仅作补充;从用户口述沉淀时须补 1–2 个“日后自然问法”词,不只抄口述原文。 +- **自测(落盘前必做)**:模拟 2–3 个用户自然问句(凭空想“用户会怎么问”,不看词表),检查每句至少命中 `summary` 或 `includeAny` 之一;不命中则回改。 +- **`taskToTopicRules[].task`**:保持稳定 id 语义(kb 引擎按 `task` 合并),不塞长短语;召回语义由 `summary` 承载,manifest 侧由 `kb build` 生成,不手写。 + +## 3. matcher 分片字段语义 + +matcher 分片(`.Knowledge/matchers/.json`)有 4 个字段参与 `routing.match()` 判定,按「否决门 → 资格门」两级: + +- **资格门**(至少满足其一即候选): + - `includeAny`:任一短语命中(OR)——最常用 + - `includeAll`:所有短语全部命中(AND)——表达组合词/上下文约束 +- **否决门**(任一门命中即整条规则出局,**恒胜,优先于 `task` 精确命中**): + - `excludeAny`:任一短语命中(OR) + - `excludeAll`:所有短语全部命中(AND);半命中不否决 + +写作准则: + +1. **默认只写 `includeAny`**:大部分主题不需要否决词/AND 词;泛化的单概念核心词交给 `includeAny` 即可。 +2. **用 `excludeAny` 排除误路由**:当同一批 `includeAny` 词在**另一场景**下反复被错命中(如 "部署" 词被 "预演部署" 误命中),用 `excludeAny: ["预演", "preview"]` 精确排除,**保留召回率**——比删触发词代价小得多。 +3. **用 `includeAll` 表达组合词**:仅当**两个词单独都太泛、但同时出现才有意义**时使用(如 "数据 + 迁移");否则拆成两条 `includeAny` 更好。 +4. **用 `excludeAll` 表达"仅当同时否定"**:仅当"任一词单独出现都不应否决,但两个词同时出现明确不属于本任务域"时使用(如 "归档 + 下线");这是罕见场景,大多数否决需求用 `excludeAny` 就够。 +5. **否决优先于 `task` 精确命中**:即使调用方显式传入 `task: "deploy"`,只要 `excludeAny` 命中,该规则仍然出局——排除词表达的是「此请求不属于该任务域」,语义强于精确命中。**若你不想让 `task` 参数被否决,不要给这条 rule 写 `excludeAny`**。 +6. **打分池合并**:`includeAll` 命中的短语与 `includeAny` 命中一起进入打分池,`score` 取最高短语分,不改变 `confidence` 分档规则(exact task=high、≥30=medium、<30=low)。 + +阈值提示: + +- `includeAny` 词表超过 12 个通常是主题过宽信号(见第 6 节拆分策略);若通过 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估。 +- 排除词无阈值约束,但写超过 5 条通常意味着资格词已过泛,应回头收紧资格门。 + +实现:`packages/core/lib/routing.js:match()`;契约测试:`scripts/test-routing-semantics.js`(10 组用例覆盖 4 字段判定门与 exact-task 交互)。 + +## 4. topicMetadata 判定准则 `topicMetadata` 是治理元数据,只影响盘点、审计和阅读预期;不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性。执行强制性以 `AGENTS.md`、rules、skills 与 topic 正文明确要求为准。 @@ -60,7 +96,7 @@ description: Flow2Spec 主题创作准则:topic 命名 / 骨架 / topicMetadat 禁止:为了分类创建、重命名、拆分 topic;在 topic markdown 正文或 `index.md` 中重复写分类块。 -## 4. topicDependencies 判定准则 +## 5. topicDependencies 判定准则 设当前主题为 A、候选依赖为 B。**四问命中任一即声明 `A → B`**: @@ -80,14 +116,14 @@ description: Flow2Spec 主题创作准则:topic 命名 / 骨架 / topicMetadat **判定时机**:终稿与新 / 改 topic 落盘后,扫正文中**反引号引用的其他 topic id 与规则文件名**,逐个套四问;命中即写入 `manifest-routing.topicDependencies`,**并在新 topic 正文显式写依赖声明**(见骨架第 4 条)。 -## 5. 大功能拆分策略 +## 6. 大功能拆分策略 当一个业务功能体量较大时,推荐「主 topic + 子 topic」结构,而非单个大 topic。 **何时拆分(软约束,满足任一评估是否需拆)**: - 对应 stock-doc 超过 **300–500 行**:建议评估拆分,不强制阻断; -- matcher `includeAny` 超过 **12 个**:主题过宽信号; +- matcher `includeAny` 超过 **12 个**:主题过宽信号(若用 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估,详见第 3 节); - topic 正文包含超过 **3 个不相干职责域**的二级标题; - `f2s-kb-upgrade` 审计时发现同一 topic 被多种不相干任务类型反复命中。 @@ -99,10 +135,10 @@ description: Flow2Spec 主题创作准则:topic 命名 / 骨架 / topicMetadat **不要做的事**: -- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 4 节反向排除); +- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 5 节反向排除); - 不为拆分而强行制造子 topic,若子模块本身不会被独立路由命中,不必建 topic。 -## 6. rule 是否需新建对应 topic +## 7. rule 是否需新建对应 topic 判据:**该 rule 是否会作为用户任务路由命中**。 @@ -111,7 +147,7 @@ description: Flow2Spec 主题创作准则:topic 命名 / 骨架 / topicMetadat 误区:「重要的规则就该有 topic」——重要不等于"用户路由命中";让消费方 SKILL 在正文里直接 `Read rules/.*` 全文即可,无需走 manifest 路由。 -## 7. 写盘权属(指针) +## 8. 写盘权属(指针) `manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/topics/*.md` 的写权约束**以 `f2s-flow2spec-unified-entry` 与各 SKILL 内「写权硬约束」为准**,本条不复述;遇分歧以统一入口与对应 SKILL 为准。 @@ -120,7 +156,7 @@ description: Flow2Spec 主题创作准则:topic 命名 / 骨架 / topicMetadat - 在未读本条的情况下新增 / 修改 topic 或 `topicDependencies`。 - 为补分类单独创建、重命名或拆分 topic。 - 在 topic 正文或 `index.md` 中写 `## 概念分类` 等 metadata 副本。 -- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 4 条)。 +- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 7 条)。 - 用 `topicDependencies` 表达"信息相关"(应通过 `index.md` 语义边界 + matcher 关键词补召回,而非依赖边)。 - 在 `topicDependencies` 中写传递冗余边或形成环。 - **在 topic 的「长文背景 / 详细资料 / 相关资料 / 长文来源 / 参考文档」等指向长文源的整节引用槽位里,列出 `.Knowledge/req-docs/*`**(含澄清 / 技术方案 / SQL / PRD)。此类槽位只允许指向 `.Knowledge/stock-docs/*_终稿.md`;无终稿时须先生成再回填。短句/佐证式的偶发点引不受此约束。 \ No newline at end of file diff --git a/.claude/skills/f2s-kb-addRules/SKILL.md b/.claude/skills/f2s-kb-addRules/SKILL.md index 93bc55f..f62b94d 100644 --- a/.claude/skills/f2s-kb-addRules/SKILL.md +++ b/.claude/skills/f2s-kb-addRules/SKILL.md @@ -83,7 +83,7 @@ description: 把用户口述的规则沉淀进知识库,自动判定「新建 - 补 `manifest-routing.topicPaths`:` -> .Knowledge/topics/.md`; - 按需补 `manifest-routing.topicMetadata`:口述规则主题通常为 `{ "primary": "policy", "confidence": "inferred" }`;用户明确确认分类可写 `manual`;如同时包含配置项 / 模块 / 能力性质,可写入不与 `primary` 重复的 `tags`;证据不足则不写 metadata,并在摘要列为待确认。分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性; - 视情况补 `taskToTopicRules[]`——**仅当**该规则会作为**用户任务路由命中**(参见 `f2s-topic-authoring` 第 5 节判据)才补;纯被其它规则 / SKILL 引用的内部规则**不进** `taskToTopicRules`; - - 若补了 `taskToTopicRules[]`,须新建 `.Knowledge/matchers/.json`,从用户口述中抽取 `includeAny` 关键词(用户原话 + 1–2 个明显近义说法,宁缺勿滥); + - 若补了 `taskToTopicRules[]`,须新建 `.Knowledge/matchers/.json`,`includeAny` 按 `f2s-topic-authoring`「初筛召回规范」抽取:**单概念核心词优先**,复合词仅作补充;除用户口述原词外须补 1–2 个“日后自然问法”词,并用 2–3 个模拟问句自测命中;topic frontmatter `summary` 同步按该规范写(kb build 会将其同步进 rule.summary 作初筛锚); - **并入存量主题**: - `topicPaths` 不变; - 可按需补齐该 topic 的 `topicMetadata`,但不得为了分类创建、重命名或拆分 topic; diff --git a/.claude/skills/f2s-kb-build/SKILL.md b/.claude/skills/f2s-kb-build/SKILL.md index bda9c7b..15b5ec2 100644 --- a/.claude/skills/f2s-kb-build/SKILL.md +++ b/.claude/skills/f2s-kb-build/SKILL.md @@ -47,9 +47,10 @@ description: 根据 .Knowledge/stock-docs 文档生成知识路由主题与索 从文档中提炼: - 主题名与主题意图(可形成 topic id) +- frontmatter `summary`(初筛召回锚:职责 + 用户会问的核心名词,按 `f2s-topic-authoring`「初筛召回规范」写;kb build 会同步进 manifest rule.summary) - 核心概念与关键流程 - 业务规则与边界条件 -- 任务触发词(写入对应 `matchers/.json` 的 `includeAny`) +- 任务触发词(写入对应 `matchers/.json` 的 `includeAny`;单概念核心词优先,复合词仅作补充) - 与现有主题的依赖关系(用于 `topicDependencies`) > **创作侧准则**:本步骤涉及新增 / 修改 topic 与 `topicDependencies`,**须先 Read** `rules/f2s-topic-authoring.*` 全文(**Cursor/Claude**:`rules/f2s-topic-authoring.mdc`;**Codex**:`.codex/topics/f2s-topic-authoring.md`),再继续步骤 3 / 步骤 5。命名、骨架、依赖判定、DAG 最小化、判定时机均以该条为准,本 SKILL 不复述。 diff --git a/.claude/skills/f2s-kb-distill/SKILL.md b/.claude/skills/f2s-kb-distill/SKILL.md index c2f923b..496ebf5 100644 --- a/.claude/skills/f2s-kb-distill/SKILL.md +++ b/.claude/skills/f2s-kb-distill/SKILL.md @@ -285,12 +285,14 @@ description: 从问答过程中提取可复用知识事实并自动入库;根 - 创建对应的 `matchers/.json`,包含: - 从用户问题中提取的关键词 - 从回答中提取的术语 - - 建议 `includeAny`:5-10 个触发词 + - 建议 `includeAny`:5-10 个触发词(单概念核心词优先,复合词仅作补充;见 `f2s-topic-authoring`「初筛召回规范」) - 在 `taskToTopicRules` 中添加路由规则 + - topic frontmatter `summary` 按「初筛召回规范」写(职责 + 用户会问的核心名词;kb build 会同步进 rule.summary) - 如果更新既有 topic: - 检查 matcher 是否需要补充新的触发词 - 从用户问题中提取未覆盖的关键词,追加到 `includeAny` + - 若本轮问法未被该 topic 的 `summary` 覆盖,回改 frontmatter `summary` 补上核心名词 #### 5.2 更新 index.md @@ -373,7 +375,7 @@ description: 从问答过程中提取可复用知识事实并自动入库;根 - 不需要用户确认(问答已验证知识的正确性) - 保持轻量,单次问答的知识提取在 30 秒内完成 - 避免过度拆分:除非下钻深度 ≥ 深且知识描述深度 ≥ 详细级,否则优先补充既有 topic -- 生成的 matcher includeAny 应覆盖用户实际会用的表述,不只是技术术语 +- 生成的 matcher includeAny 应覆盖用户实际会用的表述,不只是技术术语;topic frontmatter `summary` 同步覆盖本轮问法的核心名词 ## 完成后自检 diff --git a/.claude/skills/f2s-kb-sync/SKILL.md b/.claude/skills/f2s-kb-sync/SKILL.md index 6d69ea2..922569a 100644 --- a/.claude/skills/f2s-kb-sync/SKILL.md +++ b/.claude/skills/f2s-kb-sync/SKILL.md @@ -87,7 +87,7 @@ description: 可显式给出能力或零输入推断;先输出知识库更新 - `.Knowledge/topics/*.md` - `.Knowledge/index.md`(同步主题路由表的“关联文档(摘要)”列) -- 路由清单(按需);若创建新 topic,须同步 `topicPaths`、必要的 `taskToTopicRules` / matcher 分片;可在证据明确时写 `topicMetadata`,但分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性,不得为了分类创建、重命名或拆分 topic +- 路由清单(按需);若创建新 topic,须同步 `topicPaths`、必要的 `taskToTopicRules` / matcher 分片;新建 / 更新 topic 的 frontmatter `summary` 与 matcher `includeAny` 按 `f2s-topic-authoring`「初筛召回规范」写(summary 由 kb build 同步进 rule.summary);可在证据明确时写 `topicMetadata`,但分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性,不得为了分类创建、重命名或拆分 topic - `.Knowledge/stock-docs/*.md`(按需补充索源文档) ### 步骤 4:收尾摘要 diff --git a/.claude/skills/f2s-kb-upgrade/SKILL.md b/.claude/skills/f2s-kb-upgrade/SKILL.md index 8b94ed1..1871347 100644 --- a/.claude/skills/f2s-kb-upgrade/SKILL.md +++ b/.claude/skills/f2s-kb-upgrade/SKILL.md @@ -12,12 +12,12 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 边界(避免误区) - **`flow2spec init` 不写业务知识**:不替代 `f2s-kb-add`、`f2s-kb-fix`、`f2s-kb-feat`、`f2s-kb-sync`、`f2s-kb-build` 等对 `stock-docs` / `req-docs` / `topics` 正文与业务向路由词条的维护。 -- 本技能跑通的是 **包版本下的目录、模板占位、路由结构对齐**;用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 +- 本技能跑通的是 **Template Version 下的目录、模板占位、路由结构对齐**;Core 代码版本与 Template Version 独立。用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 - 本技能负责存量 `topicMetadata` 审计:`primary` / `tags` 仅用于治理、审计、盘点和阅读预期,不参与路由命中或执行强制性;执行强制性仍以 `AGENTS.md`、rules、skills 与 topic 正文为准。 ## 包侧发版纪律(`projectRev` 必须正确 bump) -**字段位置**:`templates/{zh-CN,en-US}/knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 +**字段位置**:Core 包模板 `knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 **字段写入语义(必读)**: - **包侧**:维护者按下文规则手动 bump(包模板自身的 `projectRev` 永远是最新值)。 @@ -27,15 +27,15 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( - 这使「项目侧 `projectRev`」语义清晰:**「本项目已基线对齐到的包模板修订号」**,而非"上次 init 时碰到的"。 **必须 bump 的修改**(每次发版至少 `+1`): -- 包模板 `templates//knowledge/topics/.md` 任一文件的**正文**修改、新增、删除或改名; -- 包模板 `templates//knowledge/matchers/.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; -- 包模板 `templates//knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; -- 包模板 `templates//knowledge/index.md` 「主题一览」节或包级章节修改。 +- 包模板 `knowledge/topics/.md` 任一文件的**正文**修改、新增、删除或改名; +- 包模板 `knowledge/matchers/.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; +- 包模板 `knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; +- 包模板 `knowledge/index.md` 「主题一览」节或包级章节修改。 **不需要 bump 的修改**: - 包源码(`lib/`、`cli.js`、`scripts/`)、`AGENTS.md`、`README*` 文档; -- `templates//flow2spec.config.json` 默认值; -- `templates//rules/*` / `templates//skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程)。 +- `flow2spec.config.json` 默认值; +- `rules/*` / `skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程,但仍须显式提升 Template Version 以便 Hook 分发新版内容)。 **判定准则一句话**:模板里 `knowledge/` 目录下 topic / matcher / manifest / index 任一**主题层产物**变了 → 必 bump;否则不动。漏 bump 会让用户的 `f2s-kb-upgrade` 跑快速路径,错过包带来的主题变更。 @@ -54,11 +54,12 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 非主题版本更新:agent 可直接代跑 `init`(无需进入本技能) -版本检查提示「知识库版本低于最新包版本」时,agent 可**直接替用户执行** `flow2spec init <已初始化的 agents>` 完成更新: +版本检查提示 Core 更新但 `templateUpdateAvailable=false` 时,agent 可**直接替用户更新 Core 并执行** `flow2spec init <已初始化的 agents>`: - `init` 后读项目侧 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` **相等**(非主题版本更新)→ 更新完成,删除 `.Knowledge/update-check.json`,**无需进入本技能**; - 两者**不等**(包含主题层变更)→ 进入本技能完整流程(从步骤 2c 起判定,不重复 `init`)。 - 该路径与本技能步骤 2c「快速路径」同一判定口径;用户显式要求「完整流程 / 覆盖重置」时仍按本技能全文执行。 +- `.Knowledge/manifest-routing.json.version` 表示 Template Version;禁止拿它与 Core Version 直接比较。 ## 旧版布局(V1)不再内置迁移 @@ -97,39 +98,30 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 强制流程 -### 步骤 -1:全局 flow2spec 版本预检(必须,先于一切,主 agent 前台探测) +### 步骤 -1:CLI/Core/Template 版本预检(必须,先于一切) -**目的**:让「能用全局 `flow2spec` 就用全局」,只在**没装**或**版本过旧**时才动手升级;已装且已是 latest 时**完全跳过**升级动作,同时决定步骤 2 命令的**默认形态**(用 `flow2spec init` 还是 `npx @latest init`)。 - -**动作**:主 agent 在进入步骤 0 **之前**,**顺序、前台**执行以下 3 条探测(都是纯查询,无副作用,秒级返回;无需拆子 agent): +主 agent 前台执行只读探测: ```bash -# 1. 探测本机全局是否装了 flow2spec -flow2spec --version 2>/dev/null || echo __F2S_NOT_INSTALLED__ -# 2. 查询 npm 上 latest 版本号(网络受限时可能失败,允许失败) -npm view @double-coding/flow2spec version 2>/dev/null || echo __F2S_NPM_UNREACHABLE__ -# 3. (备用)若第 1 步返回 __F2S_NOT_INSTALLED__,用来确认 npx 可用 -command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ +flow2spec version +flow2spec update --check ``` -**判定 3 分支**(按结果选一条,写入本轮上下文并影响步骤 2 与步骤 5 摘要): - -| 情况 | 判定条件 | 行动 | 步骤 2 命令默认形态 | -| --- | --- | --- | --- | -| **A. 已装且是 latest** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V === L` | **完全跳过升级**,本轮不派子 agent、不跑 `npm i -g` | **`flow2spec init `**(用全局) | -| **B. 已装但落后** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V !== L`(`V < L` 或 semver 不等) | **派独立子 agent 后台跑** `npm i -g @double-coding/flow2spec@latest`(fire-and-forget,不等待,不阻塞主流程);本轮步骤 2 仍用 `npx @latest` 保证本次拿到 latest 模板 | **`npx @double-coding/flow2spec@latest init `** | -| **C. 未装 or 版本无法确认** | 第 1 步命中 `__F2S_NOT_INSTALLED__`,或第 2 步命中 `__F2S_NPM_UNREACHABLE__` 且第 1 步也未拿到版本号 | 若 A 情况「已装 latest」不成立且**未装**:派独立子 agent 后台跑 `npm i -g ...@latest`(同 B);若第 2 步失败但第 1 步已装某版本:视作 B 且无法比对 latest,**不派**升级、仅提示「latest 未知,保守用 npx」 | **`npx @double-coding/flow2spec@latest init `** | +按输出记录 CLI Version、Core Version、Core Pinned、Template Version、Protocol Version,以及 npm 最新 Core/Template(CLI 对 Core 为精确 pin,两包联动发布): -**编排(必须)**: +| 情况 | 行动 | 步骤 2 默认命令 | +| --- | --- | --- | +| **A. Template 已是最新** | 若 CLI/Core 有更新,执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新)后幂等 init 刷新 Hook;随后**不得直接停止**,必须先做下方「**项目侧对齐检查**」,通过后才删除缓存并停止本技能 | `flow2spec init ` | +| **B. Template 有更新** | 执行 `flow2spec update --cli`(CLI 与配套 Core 一起到位),继续步骤 0 | `flow2spec init ` | +| **C. 未安装或版本未知** | 使用 latest CLI(自带 pin 的配套 Core),避免 npx 复用旧版缓存 | `npx --yes @latest init ` | -- **A 分支**:主 agent 直接跳过所有升级动作,**不派**子 agent;本轮步骤 2 命令首选 `flow2spec init`。 -- **B / C 分支**:若确需升级(未装或版本落后),派**独立子 agent** fire-and-forget 执行 `npm i -g @double-coding/flow2spec@latest`,**不等待完成**、**不阻塞**主流程;成败均不进入 SKILL 结论。该派子**强制**执行,**不受** `flow2spec.config.json.subAgent` 字段约束(全局 npm 装包不属业务拆分范畴)。 -- **写权**:子 agent 仅执行该 shell,**不**触碰 `.Knowledge` / `manifest-routing.json` / `index.md` 等任何项目文件;写权硬约束不变。 -- **探测失败兜底**:若 3 条探测全部失败(无 shell 权限、极端受限环境),按 C 分支处理并用 `npx @latest`;此时也可以直接放弃步骤 -1、把升级留给 `cli.js` 的 `maybeAutoUpdateGlobalInstall()` 收尾兜底。 +预检失败时允许回退 C,但不得把 Core Version 当作 Template Version。此步骤不强制创建子 agent,也不后台安装全局包。 -**与 cli.js 的关系**: +**项目侧对齐检查(A 分支停止前必须)**:`flow2spec version` / `update --check` 对比的是**本机包 vs npm 最新**,不反映**本项目 `.Knowledge` 是否已对齐当前包模板**(典型误判:刚升级完 CLI/Core 后首次在老项目跑本技能,包侧「均为最新」但项目侧模板仍是旧版,直接停止会跳过全部升级动作)。停止前逐项确认: -- `cli.js` 内 `maybeAutoUpdateGlobalInstall()` 是 `init` 收尾兜底逻辑,**与本步不冲突**:本步在前台 init 之前完成探测/派工,cli 那段在 init 收尾时再兜一次;两次都成功就是 no-op,第一次失败第二次还能补救。 +1. Read 项目侧 `.Knowledge/manifest-routing.json`:若 `version`(项目侧 Template Version)**低于**本机包 Template Version,或 `pkgRev` / `projectRev` 字段缺失 → 项目侧未对齐,**视同 Template 更新**:继续步骤 0 → 2(init)→ 2c 判定,不得停止; +2. 字段一致时再执行 `flow2spec kb check --strict`:若报 summary 质量 warning 或结构问题 → 转步骤 3a.7 / 3a.8 处理完再收尾; +3. 以上均通过才允许按 A 分支停止,并在摘要中写明「项目侧已对齐(version=X,strict 通过)」。 ### 步骤 0:版本判定与分流(必须,先于 init) @@ -165,10 +157,10 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 在目标项目根目录执行以下命令(**按步骤 -1 的分支结论选默认形态**): -1. **步骤 -1 判定为 A(已装且是 latest)**:直接用全局 CLI(**首选**): +1. **步骤 -1 判定为 A/B(本地 CLI/Core 可用)**:直接使用当前 CLI: - `flow2spec init ` -2. **步骤 -1 判定为 B/C(未装 / 落后 / latest 未知)**:拉 npm latest 跑(**保证本次拿到最新模板**): - - `npx @double-coding/flow2spec@latest init ` +2. **步骤 -1 判定为 C**:用 latest CLI(自带 pin 的配套 Core 与模板): + - `npx --yes @latest init ` 3. 覆盖重置时: - 在上述命令末尾追加 `--reset-knowledge` 4. 用户显式要求切换模板语言时: @@ -177,7 +169,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ > `` 示例:`cursor claude codex`。 -> **辅助命令(用户可自查)**:`flow2spec --version` 看当前全局版本;`flow2spec update` 触发 CLI 内置的自更新。这两条**不**替代本 SKILL 的完整流程——它们只是「让全局 CLI 保鲜」,主题层对齐仍须走本 SKILL 步骤 2 及以后。 +> **辅助命令(用户可自查)**:`flow2spec version` 查看五维版本;`flow2spec update --check` 检查更新,`flow2spec update --cli` 整体更新(CLI 与配套 Core 联动,`--core` 为其等价别名)。这些命令不替代 Template Version 变化后的本技能完整流程。 **步骤 2 完成后**:立刻执行上文 **「init 与技能自更新」**:重读 **`skills/f2s-kb-upgrade/SKILL.md`**;若有更新则**按新版字面从步骤 2c 起重跑**(**不再次 init**;避免用旧版 SKILL 做后续校验)。 @@ -243,7 +235,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ - `includeAny` 词数超过 **12 个**; - topic 正文包含超过 **3 个不相干职责域**的二级标题; - 该 topic 同时被多种不相干任务类型频繁命中(可从 `taskToTopicRules` 和 matcher 词宽度判断)。 -7. **旧 topic frontmatter 自动补齐**:完整流程中必须由 agent 自行执行 `flow2spec kb build --fix-topics`(或等价内部能力),为缺少 frontmatter / `revision` 的存量 topic 补 `id`、`revision`、`summary`,并按 `manifest-routing.json` 补 `dependsOn` / `primary` / `confidence` / `tags`。随后执行 `flow2spec kb check --strict`;若 strict 失败,停止并在摘要中列出具体 topic / reason。不得要求用户手动逐个 topic 添加头部。 +7. **旧 topic frontmatter 自动补齐**:完整流程中必须由 agent 自行执行 `flow2spec kb build --fix-topics`(或等价内部能力),为缺少 frontmatter / `revision` 的存量 topic 补 `id`、`revision`、`summary`,并按 `manifest-routing.json` 补 `dependsOn` / `primary` / `confidence` / `tags`。随后执行 `flow2spec kb check --strict`;若失败项为 **summary 质量 warning**(缺失 / 占位 / 超长),转第 8 条补写;其余失败停止并在摘要中列出具体 topic / reason。不得要求用户手动逐个 topic 添加头部。 +8. **summary 初筛锚补写(必须,agent 才能写出语义摘要)**:对 `kb check --strict` 报出的每个 summary 质量 warning,逐个 Read 对应 `.Knowledge/topics/.md` 正文,按 `f2s-topic-authoring`「初筛召回规范」补写 frontmatter `summary`(职责 + 用户会问的核心名词,软 30 字 / 硬 40 字);全部补完后执行 `flow2spec kb build` 同步进 `rule.summary`,再跑 `flow2spec kb check --strict` 直至无 summary warning。禁止用占位文案(`(路由摘要)` / TODO)敷衍通过。 ### 步骤 3b:`index.md` 融合与 `template/index.template.md`(必须执行) @@ -300,7 +293,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 输出以下信息: -- **步骤 -1 全局版本预检**:分支结论(`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级) / C 未装或 latest 未知(已派或提示)`)+ 当前全局版本 + npm latest 版本(若拿到) +- **步骤 -1 全局版本预检**:分支结论(`A 已装且是 latest(需附项目侧对齐结论) / B 已装但落后(已派子 agent 后台升级) / C 未装或 latest 未知(已派或提示)`)+ 当前全局版本 + npm latest 版本(若拿到) +- **项目侧对齐检查**(A 分支时必填):`已对齐(version=X,strict 通过,已停止)` / `未对齐(version X < 包 Y,已转完整流程)` / `strict 报 warning(已转 3a.7/3a.8)` - 执行命令(含 agent 与是否 reset) - 是否成功 - **`projectRev` 判定**:`projectRev` X vs `pkgRev` Y → 快速路径 / 完整流程 / 字段缺失走兜底(步骤 2c) @@ -319,8 +313,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ ```markdown ## f2s-kb-upgrade 执行结果 -- **步骤 -1 全局版本预检**:`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=``,latest=`` -- 本技能内代跑命令:`<实际执行的 flow2spec init ... 或 npx @latest init ...>` +- **步骤 -1 全局版本预检**:`A 已装且是 latest(项目侧对齐:<结论>) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=``,latest=`` +- 本技能内代跑命令:`<实际执行的 flow2spec init ... 或显式 latest CLI/Core 组合 init ...>` - init 模式:`增量` / `覆盖重置(--reset-knowledge)` - 执行结果:`成功` / `失败` - **主题层判定**:`projectRev=` vs `pkgRev=` → `快速路径(已跳过 3/3a/3b)` / `完整流程` / `字段缺失走兜底` @@ -331,6 +325,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ - **index(快照 + 融合)**:`快照已复制` / `index.md 已融合` / `快速路径下未执行` / `待处理(见备注)` - **topicMetadata(存量审计)**:`已补齐` / `待用户确认` / `快速路径下未执行`;列出新增 / 修正 / 删除的 topicId - **topic frontmatter**:`已自动补齐 N 个` / `已完整无需补齐` / `strict 校验失败` / `快速路径下未执行` +- **summary 初筛锚**:`已补写 N 个(已 kb build 同步 rule.summary)` / `已合规无需补写` / `快速路径下未执行` - **f2s-kb-upgrade SKILL**:`init 后无变化` / `已按新版从 2c 起重跑 N 轮(不再次 init)` / `快速路径下跳过该闭环` / `待确认` - **`projectRev` 回写**:`已写入项目 manifest(值=pkgRev)` / `快速路径下未执行` / `pkgRev=null 未动` - manifest-routing / matchers 分片:`已与模板对齐` / `已是最新` / `reset 覆盖` @@ -351,7 +346,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ ## 完成后自检 -1. 是否已做 **步骤 -1**:在进入步骤 0 前**已顺序前台执行 3 条探测**(`flow2spec --version` / `npm view ... version` / `npx` 可用性),并按 A/B/C 分支得出结论;仅在 B/C 时才**派独立子 agent**后台跑 `npm i -g @double-coding/flow2spec@latest`(不等待),A 分支**未派**任何升级动作;步骤 2 命令默认形态是否随分支选定(A→`flow2spec init`,B/C→`npx @latest init`);摘要中已写清分支与版本对比。 +1. 是否已做 **步骤 -1**:执行 `flow2spec version` 与 `flow2spec update --check`,记录 CLI/Core/Core Pinned/Template/Protocol;有更新时是否执行 `flow2spec update --cli` 联动刷新 CLI 与配套 Core,Template 更新是否按 A/B/C 选择当前 CLI 或 latest CLI;**A 分支停止前是否完成「项目侧对齐检查」**(manifest `version`/`pkgRev` 对比 + `kb check --strict`),未对齐时是否已转完整流程而非直接停止。 2. 是否已做 **步骤 0**:V1 已停止执行并告知用户处理方式、**现行库(V2+)** 正常进入 `init` 流程。 3. 是否在 **步骤 2 开始前** 记录了项目侧 `projectRev`(`projectRev`),并在 **步骤 2 的 `init` 之后** 重读 `pkgRev`、执行 **步骤 2c** 判定。 4. 是否在 **步骤 2 的 `init` 之后**重读过 **`f2s-kb-upgrade/SKILL.md`**:完整流程下有变化必须**按新版字面从步骤 2c 起重跑**(**不再次 init**);快速路径下可跳过该闭环(见「init 与技能自更新」「快速路径例外」)。 @@ -359,7 +354,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 6. 是否明确标注增量 or reset 模式。 7. **完整流程时**:是否已处理旧主题文件清理与 `index/manifest` 引用修复(步骤 3)。 8. **完整流程时**:是否已执行 **步骤 3a**:审计 `topicMetadata`,确保无孤儿 key / 非法 primary / 非法 confidence;缺失旧主题已按证据补 `inferred` 或列为待确认。 -9. **完整流程时**:是否已执行 `flow2spec kb build --fix-topics` 或等价内部能力,并随后执行 `flow2spec kb check --strict`,确保存量 topic 已具备 `revision`。 +9. **完整流程时**:是否已执行 `flow2spec kb build --fix-topics` 或等价内部能力,并随后执行 `flow2spec kb check --strict`,确保存量 topic 已具备 `revision`;summary 质量 warning 是否已按步骤 3a.8 补写清零并 `kb build` 同步 `rule.summary`。 10. **完整流程时**:是否已执行 **步骤 3b**:**融合** `index.md`(**主题一览**节起至命中与执行前为项目维护区,其余同包版),并核对 `topicPaths`;**完整流程末尾**是否已**回写** 项目侧 `projectRev = pkgRev`(`pkgRev=null` 则保留原值)。 11. **快速路径时**:步骤 3 / 3a / 3b 是否真的跳过(未做无关扫描),摘要中明确标注「快速路径下未执行」。 12. 是否输出了 manifest 与关键路径校验结果。 diff --git a/.codex/hooks/f2s-update-check.js b/.codex/hooks/f2s-update-check.js index ad5436f..208c7b8 100644 --- a/.codex/hooks/f2s-update-check.js +++ b/.codex/hooks/f2s-update-check.js @@ -1,189 +1,156 @@ #!/usr/bin/env node 'use strict'; -/** - * flow2spec SessionStart hook — 每天第一次对话时检查版本更新。 - * 比较本地知识库 manifest-routing.json 的 version 与 npm 最新版本: - * - 一致或本地更新 → 静默退出 - * - 落后 → 向 Agent 上下文注入提示(agent 可直接代跑 flow2spec init;主题层变更再走 f2s-kb-upgrade) - * 已检查过且无需升级则静默;已检查且仍需升级时,每个新会话继续注入提醒。 - * 由 flow2spec init 写入对应 agent 的 hooks/f2s-update-check.js。 - */ -const fs = require('fs'); + +const fs = require('fs'); const path = require('path'); const { execFileSync } = require('child_process'); const MANIFEST_PATH = path.join(process.cwd(), '.Knowledge', 'manifest-routing.json'); -const CACHE_DIR = path.join(process.cwd(), '.Knowledge'); -const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json'); +const CACHE_FILE = path.join(process.cwd(), '.Knowledge', 'update-check.json'); const PACKAGE_NAME_PLACEHOLDER = '__FLOW2SPEC_' + 'PACKAGE_NAME__'; const PACKAGE_NAME = '@double-coding/flow2spec-core'; +const GENERATED_CORE_VERSION = '3.7.2'; +const GENERATED_TEMPLATE_VERSION = '3.6.2'; -// ── 缓存 ──────────────────────────────────────────────────────────────────── - -function readCache() { - if (!fs.existsSync(CACHE_FILE)) return null; - try { - const d = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); - if (!d || typeof d !== 'object') return null; - const checkedAt = Number(d.checkedAt || 0); - if (!checkedAt) return null; - if (new Date(checkedAt).toDateString() !== new Date().toDateString()) return null; - return d; - } catch (_) { return null; } +function readJson(file) { + try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { return null; } } -function getProjectName() { - try { - const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); - if (pkg && pkg.name) return String(pkg.name); - } catch (_) {} - return path.basename(process.cwd()); +function parseVer(value) { + return String(value || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((part) => { + const number = Number.parseInt(part, 10); + return Number.isFinite(number) ? number : 0; + }); } -function buildNotice(latestNpm, manifestVersion) { - const userMsg = `📦 [flow2spec] 当前项目「${getProjectName()}」知识库版本 v${manifestVersion},低于最新包版本 v${latestNpm}。我可以直接代跑 flow2spec init 完成更新;若本次更新包含主题层变更,再走 f2s-kb-upgrade 完整流程。`; - return [ - `[flow2spec/agent-instruction] 你必须在本轮回复开头将下列升级提示原文转告用户,不得省略、不得仅在内部判断后跳过:`, - ``, - userMsg, - ``, - `[flow2spec/agent-instruction] 转告后可直接替用户执行 \`flow2spec init <已初始化的 agents,如 claude cursor codex>\` 完成更新,无需用户手动执行 f2s-kb-upgrade。init 成功后读取 .Knowledge/manifest-routing.json:若 projectRev 与 pkgRev 相等(非主题版本更新),更新已完成,删除 .Knowledge/update-check.json;若两者不等(包含主题层变更),继续执行 f2s-kb-upgrade skill 完整流程(可从其步骤 2c 起,不必重复 init)。`, - ].join('\n'); +function cmpVer(left, right) { + const a = parseVer(left), b = parseVer(right); + for (let index = 0; index < 3; index += 1) { + const difference = (a[index] || 0) - (b[index] || 0); + if (difference !== 0) return difference; + } + return 0; } -function emitNotice(notice) { - process.stdout.write( - JSON.stringify({ - additional_context: notice, - hookSpecificOutput: { - hookEventName: 'SessionStart', - additionalContext: notice, - }, - }) + '\n' - ); +function getProjectName() { + return readJson(path.join(process.cwd(), 'package.json'))?.name || path.basename(process.cwd()); } -function writeCache(latestNpm, manifestVersion) { - try { - const needsUpgrade = cmpVer(manifestVersion, latestNpm) < 0; - fs.writeFileSync( - CACHE_FILE, - `${JSON.stringify({ - latestNpm, - manifestVersion, - needsUpgrade, - notice: needsUpgrade ? buildNotice(latestNpm, manifestVersion) : '', - checkedAt: Date.now(), - }, null, 2)}\n`, - 'utf8' - ); - } catch (_) {} +function getManifestVersion() { + return readJson(MANIFEST_PATH)?.version || null; } -function deleteCache() { - try { - if (fs.existsSync(CACHE_FILE)) fs.unlinkSync(CACHE_FILE); - } catch (_) {} +function getPackageName() { + return PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER + ? PACKAGE_NAME + : '@double-coding/flow2spec-core'; } -// ── 版本比较 ───────────────────────────────────────────────────────────────── - -function parseVer(v) { - return String(v || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((p) => { - const n = Number.parseInt(p, 10); - return Number.isFinite(n) ? n : 0; - }); +function isEnabled() { + const config = readJson(path.join(process.cwd(), 'flow2spec.config.json')); + return config?.updateCheck?.enabled !== false; } -/** a < b → 负数;a === b → 0;a > b → 正数 */ -function cmpVer(a, b) { - const av = parseVer(a), bv = parseVer(b); - for (let i = 0; i < 3; i++) { - const d = (av[i] || 0) - (bv[i] || 0); - if (d !== 0) return d; - } - return 0; +function readCache() { + const cache = readJson(CACHE_FILE); + if (!cache?.checkedAt) return null; + return new Date(cache.checkedAt).toDateString() === new Date().toDateString() ? cache : null; } -// ── 读取 ───────────────────────────────────────────────────────────────────── +function queryLatestMetadata(packageName) { + const output = execFileSync( + 'npm', + ['view', packageName, 'version', 'templateVersion', '--json', '--registry=https://registry.npmjs.org'], + { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }, + ); + const metadata = JSON.parse(output); + const latestCoreVersion = typeof metadata === 'string' ? metadata : metadata.version; + const latestTemplateVersion = typeof metadata === 'string' + ? metadata + : metadata.templateVersion || metadata.version; + return { latestCoreVersion, latestTemplateVersion }; +} -function getManifestVersion() { - if (!fs.existsSync(MANIFEST_PATH)) return null; - try { - return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8')).version || null; - } catch (_) { return null; } +function updateState(metadata, manifestVersion) { + const coreUpdateAvailable = cmpVer(GENERATED_CORE_VERSION, metadata.latestCoreVersion) < 0; + const templateUpdateAvailable = cmpVer(manifestVersion, metadata.latestTemplateVersion) < 0; + return { + packageName: getPackageName(), + generatedByCoreVersion: GENERATED_CORE_VERSION, + generatedTemplateVersion: GENERATED_TEMPLATE_VERSION, + manifestVersion, + ...metadata, + coreUpdateAvailable, + templateUpdateAvailable, + needsUpgrade: coreUpdateAvailable || templateUpdateAvailable, + }; } -function getPackageName() { - if (PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER) { - return PACKAGE_NAME; +function buildNotice(state) { + const project = getProjectName(); + const versionSummary = `Core v${state.generatedByCoreVersion} -> v${state.latestCoreVersion},Template v${state.manifestVersion} -> v${state.latestTemplateVersion}`; + if (state.templateUpdateAvailable) { + return [ + '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', + '', + `📦 [flow2spec] 当前项目「${project}」有模板更新(${versionSummary})。`, + '', + '[flow2spec/agent-instruction] 可先执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新),再执行 `flow2spec init <已初始化的 agents>`。init 后读取 `.Knowledge/manifest-routing.json`:projectRev 与 pkgRev 相等则更新完成并删除 `.Knowledge/update-check.json`;不等时再执行 f2s-kb-upgrade(可从步骤 2c 继续)。', + ].join('\n'); } - return '@double-coding/flow2spec'; + return [ + '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', + '', + `📦 [flow2spec] 当前项目「${project}」仅有 Core 程序更新(${versionSummary}),模板版本未变化。`, + '', + '[flow2spec/agent-instruction] 可执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新),随后执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook。Template Version 未变化,不进入 f2s-kb-upgrade;完成后删除 `.Knowledge/update-check.json`。', + ].join('\n'); } -function queryNpmLatest(pkgName) { - return execFileSync('npm', ['view', pkgName, 'version'], { - encoding: 'utf8', - timeout: 5000, - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); +function emitNotice(notice) { + process.stdout.write(JSON.stringify({ + additional_context: notice, + hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: notice }, + }) + '\n'); } -// ── 配置开关 ────────────────────────────────────────────────────────────────── - -function isEnabled() { +function writeCache(state) { try { - const cfg = JSON.parse(fs.readFileSync( - path.join(process.cwd(), 'flow2spec.config.json'), 'utf8' - )); - const uc = cfg && cfg.updateCheck; - if (uc && typeof uc.enabled === 'boolean') return uc.enabled; - return true; - } catch (_) { return true; } + fs.writeFileSync(CACHE_FILE, `${JSON.stringify({ + ...state, + latestNpm: state.latestTemplateVersion, + notice: state.needsUpgrade ? buildNotice(state) : '', + checkedAt: Date.now(), + }, null, 2)}\n`, 'utf8'); + } catch (_) {} } -// ── 主流程 ──────────────────────────────────────────────────────────────────── +function deleteCache() { + try { fs.rmSync(CACHE_FILE, { force: true }); } catch (_) {} +} function main() { - if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return; - if (!isEnabled()) return; - const cache = readCache(); - if (cache) { - // 今天已检查过则不重复查 npm;若缓存显示仍需升级,每个新会话继续提醒。 - const needsUpgrade = cache.needsUpgrade === true || - cmpVer(cache.manifestVersion, cache.latestNpm) < 0; - if (needsUpgrade) { - const currentManifestVersion = getManifestVersion(); - if (currentManifestVersion && cache.latestNpm && - cmpVer(currentManifestVersion, cache.latestNpm) >= 0) { - deleteCache(); - return; - } - // SessionStart 进入新会话:缓存命中且仍需升级,直接 emit。 - const notice = buildNotice(cache.latestNpm, cache.manifestVersion); - emitNotice(notice); - } - return; - } - + if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || !isEnabled()) return; const manifestVersion = getManifestVersion(); - if (!manifestVersion) return; // 无知识库,跳过 - - let latestNpm; - try { - const pkgName = getPackageName(); - latestNpm = queryNpmLatest(pkgName); - } catch (_) { - return; // 网络不通,静默退出,不写缓存(下次还会重试) + if (!manifestVersion) return; + + const cached = readCache(); + if (cached) { + const state = updateState({ + latestCoreVersion: cached.latestCoreVersion, + latestTemplateVersion: cached.latestTemplateVersion || cached.latestNpm, + }, manifestVersion); + if (!state.needsUpgrade) deleteCache(); + else emitNotice(buildNotice(state)); + return; } - // 写缓存(无论是否需要升级,今天不再重复检查) - writeCache(latestNpm, manifestVersion); - - if (cmpVer(manifestVersion, latestNpm) >= 0) return; // 已是最新 - - const notice = buildNotice(latestNpm, manifestVersion); - emitNotice(notice); + let metadata; + try { metadata = queryLatestMetadata(getPackageName()); } catch (_) { return; } + if (!metadata.latestCoreVersion || !metadata.latestTemplateVersion) return; + const state = updateState(metadata, manifestVersion); + writeCache(state); + if (state.needsUpgrade) emitNotice(buildNotice(state)); } main(); diff --git a/.codex/skills/f2s-kb-addRules/SKILL.md b/.codex/skills/f2s-kb-addRules/SKILL.md index 93bc55f..f62b94d 100644 --- a/.codex/skills/f2s-kb-addRules/SKILL.md +++ b/.codex/skills/f2s-kb-addRules/SKILL.md @@ -83,7 +83,7 @@ description: 把用户口述的规则沉淀进知识库,自动判定「新建 - 补 `manifest-routing.topicPaths`:` -> .Knowledge/topics/.md`; - 按需补 `manifest-routing.topicMetadata`:口述规则主题通常为 `{ "primary": "policy", "confidence": "inferred" }`;用户明确确认分类可写 `manual`;如同时包含配置项 / 模块 / 能力性质,可写入不与 `primary` 重复的 `tags`;证据不足则不写 metadata,并在摘要列为待确认。分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性; - 视情况补 `taskToTopicRules[]`——**仅当**该规则会作为**用户任务路由命中**(参见 `f2s-topic-authoring` 第 5 节判据)才补;纯被其它规则 / SKILL 引用的内部规则**不进** `taskToTopicRules`; - - 若补了 `taskToTopicRules[]`,须新建 `.Knowledge/matchers/.json`,从用户口述中抽取 `includeAny` 关键词(用户原话 + 1–2 个明显近义说法,宁缺勿滥); + - 若补了 `taskToTopicRules[]`,须新建 `.Knowledge/matchers/.json`,`includeAny` 按 `f2s-topic-authoring`「初筛召回规范」抽取:**单概念核心词优先**,复合词仅作补充;除用户口述原词外须补 1–2 个“日后自然问法”词,并用 2–3 个模拟问句自测命中;topic frontmatter `summary` 同步按该规范写(kb build 会将其同步进 rule.summary 作初筛锚); - **并入存量主题**: - `topicPaths` 不变; - 可按需补齐该 topic 的 `topicMetadata`,但不得为了分类创建、重命名或拆分 topic; diff --git a/.codex/skills/f2s-kb-build/SKILL.md b/.codex/skills/f2s-kb-build/SKILL.md index bda9c7b..15b5ec2 100644 --- a/.codex/skills/f2s-kb-build/SKILL.md +++ b/.codex/skills/f2s-kb-build/SKILL.md @@ -47,9 +47,10 @@ description: 根据 .Knowledge/stock-docs 文档生成知识路由主题与索 从文档中提炼: - 主题名与主题意图(可形成 topic id) +- frontmatter `summary`(初筛召回锚:职责 + 用户会问的核心名词,按 `f2s-topic-authoring`「初筛召回规范」写;kb build 会同步进 manifest rule.summary) - 核心概念与关键流程 - 业务规则与边界条件 -- 任务触发词(写入对应 `matchers/.json` 的 `includeAny`) +- 任务触发词(写入对应 `matchers/.json` 的 `includeAny`;单概念核心词优先,复合词仅作补充) - 与现有主题的依赖关系(用于 `topicDependencies`) > **创作侧准则**:本步骤涉及新增 / 修改 topic 与 `topicDependencies`,**须先 Read** `rules/f2s-topic-authoring.*` 全文(**Cursor/Claude**:`rules/f2s-topic-authoring.mdc`;**Codex**:`.codex/topics/f2s-topic-authoring.md`),再继续步骤 3 / 步骤 5。命名、骨架、依赖判定、DAG 最小化、判定时机均以该条为准,本 SKILL 不复述。 diff --git a/.codex/skills/f2s-kb-distill/SKILL.md b/.codex/skills/f2s-kb-distill/SKILL.md index c2f923b..496ebf5 100644 --- a/.codex/skills/f2s-kb-distill/SKILL.md +++ b/.codex/skills/f2s-kb-distill/SKILL.md @@ -285,12 +285,14 @@ description: 从问答过程中提取可复用知识事实并自动入库;根 - 创建对应的 `matchers/.json`,包含: - 从用户问题中提取的关键词 - 从回答中提取的术语 - - 建议 `includeAny`:5-10 个触发词 + - 建议 `includeAny`:5-10 个触发词(单概念核心词优先,复合词仅作补充;见 `f2s-topic-authoring`「初筛召回规范」) - 在 `taskToTopicRules` 中添加路由规则 + - topic frontmatter `summary` 按「初筛召回规范」写(职责 + 用户会问的核心名词;kb build 会同步进 rule.summary) - 如果更新既有 topic: - 检查 matcher 是否需要补充新的触发词 - 从用户问题中提取未覆盖的关键词,追加到 `includeAny` + - 若本轮问法未被该 topic 的 `summary` 覆盖,回改 frontmatter `summary` 补上核心名词 #### 5.2 更新 index.md @@ -373,7 +375,7 @@ description: 从问答过程中提取可复用知识事实并自动入库;根 - 不需要用户确认(问答已验证知识的正确性) - 保持轻量,单次问答的知识提取在 30 秒内完成 - 避免过度拆分:除非下钻深度 ≥ 深且知识描述深度 ≥ 详细级,否则优先补充既有 topic -- 生成的 matcher includeAny 应覆盖用户实际会用的表述,不只是技术术语 +- 生成的 matcher includeAny 应覆盖用户实际会用的表述,不只是技术术语;topic frontmatter `summary` 同步覆盖本轮问法的核心名词 ## 完成后自检 diff --git a/.codex/skills/f2s-kb-sync/SKILL.md b/.codex/skills/f2s-kb-sync/SKILL.md index 6d69ea2..922569a 100644 --- a/.codex/skills/f2s-kb-sync/SKILL.md +++ b/.codex/skills/f2s-kb-sync/SKILL.md @@ -87,7 +87,7 @@ description: 可显式给出能力或零输入推断;先输出知识库更新 - `.Knowledge/topics/*.md` - `.Knowledge/index.md`(同步主题路由表的“关联文档(摘要)”列) -- 路由清单(按需);若创建新 topic,须同步 `topicPaths`、必要的 `taskToTopicRules` / matcher 分片;可在证据明确时写 `topicMetadata`,但分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性,不得为了分类创建、重命名或拆分 topic +- 路由清单(按需);若创建新 topic,须同步 `topicPaths`、必要的 `taskToTopicRules` / matcher 分片;新建 / 更新 topic 的 frontmatter `summary` 与 matcher `includeAny` 按 `f2s-topic-authoring`「初筛召回规范」写(summary 由 kb build 同步进 rule.summary);可在证据明确时写 `topicMetadata`,但分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性,不得为了分类创建、重命名或拆分 topic - `.Knowledge/stock-docs/*.md`(按需补充索源文档) ### 步骤 4:收尾摘要 diff --git a/.codex/skills/f2s-kb-upgrade/SKILL.md b/.codex/skills/f2s-kb-upgrade/SKILL.md index 8b94ed1..1871347 100644 --- a/.codex/skills/f2s-kb-upgrade/SKILL.md +++ b/.codex/skills/f2s-kb-upgrade/SKILL.md @@ -12,12 +12,12 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 边界(避免误区) - **`flow2spec init` 不写业务知识**:不替代 `f2s-kb-add`、`f2s-kb-fix`、`f2s-kb-feat`、`f2s-kb-sync`、`f2s-kb-build` 等对 `stock-docs` / `req-docs` / `topics` 正文与业务向路由词条的维护。 -- 本技能跑通的是 **包版本下的目录、模板占位、路由结构对齐**;用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 +- 本技能跑通的是 **Template Version 下的目录、模板占位、路由结构对齐**;Core 代码版本与 Template Version 独立。用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 - 本技能负责存量 `topicMetadata` 审计:`primary` / `tags` 仅用于治理、审计、盘点和阅读预期,不参与路由命中或执行强制性;执行强制性仍以 `AGENTS.md`、rules、skills 与 topic 正文为准。 ## 包侧发版纪律(`projectRev` 必须正确 bump) -**字段位置**:`templates/{zh-CN,en-US}/knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 +**字段位置**:Core 包模板 `knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 **字段写入语义(必读)**: - **包侧**:维护者按下文规则手动 bump(包模板自身的 `projectRev` 永远是最新值)。 @@ -27,15 +27,15 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( - 这使「项目侧 `projectRev`」语义清晰:**「本项目已基线对齐到的包模板修订号」**,而非"上次 init 时碰到的"。 **必须 bump 的修改**(每次发版至少 `+1`): -- 包模板 `templates//knowledge/topics/.md` 任一文件的**正文**修改、新增、删除或改名; -- 包模板 `templates//knowledge/matchers/.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; -- 包模板 `templates//knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; -- 包模板 `templates//knowledge/index.md` 「主题一览」节或包级章节修改。 +- 包模板 `knowledge/topics/.md` 任一文件的**正文**修改、新增、删除或改名; +- 包模板 `knowledge/matchers/.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; +- 包模板 `knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; +- 包模板 `knowledge/index.md` 「主题一览」节或包级章节修改。 **不需要 bump 的修改**: - 包源码(`lib/`、`cli.js`、`scripts/`)、`AGENTS.md`、`README*` 文档; -- `templates//flow2spec.config.json` 默认值; -- `templates//rules/*` / `templates//skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程)。 +- `flow2spec.config.json` 默认值; +- `rules/*` / `skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程,但仍须显式提升 Template Version 以便 Hook 分发新版内容)。 **判定准则一句话**:模板里 `knowledge/` 目录下 topic / matcher / manifest / index 任一**主题层产物**变了 → 必 bump;否则不动。漏 bump 会让用户的 `f2s-kb-upgrade` 跑快速路径,错过包带来的主题变更。 @@ -54,11 +54,12 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 非主题版本更新:agent 可直接代跑 `init`(无需进入本技能) -版本检查提示「知识库版本低于最新包版本」时,agent 可**直接替用户执行** `flow2spec init <已初始化的 agents>` 完成更新: +版本检查提示 Core 更新但 `templateUpdateAvailable=false` 时,agent 可**直接替用户更新 Core 并执行** `flow2spec init <已初始化的 agents>`: - `init` 后读项目侧 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` **相等**(非主题版本更新)→ 更新完成,删除 `.Knowledge/update-check.json`,**无需进入本技能**; - 两者**不等**(包含主题层变更)→ 进入本技能完整流程(从步骤 2c 起判定,不重复 `init`)。 - 该路径与本技能步骤 2c「快速路径」同一判定口径;用户显式要求「完整流程 / 覆盖重置」时仍按本技能全文执行。 +- `.Knowledge/manifest-routing.json.version` 表示 Template Version;禁止拿它与 Core Version 直接比较。 ## 旧版布局(V1)不再内置迁移 @@ -97,39 +98,30 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 强制流程 -### 步骤 -1:全局 flow2spec 版本预检(必须,先于一切,主 agent 前台探测) +### 步骤 -1:CLI/Core/Template 版本预检(必须,先于一切) -**目的**:让「能用全局 `flow2spec` 就用全局」,只在**没装**或**版本过旧**时才动手升级;已装且已是 latest 时**完全跳过**升级动作,同时决定步骤 2 命令的**默认形态**(用 `flow2spec init` 还是 `npx @latest init`)。 - -**动作**:主 agent 在进入步骤 0 **之前**,**顺序、前台**执行以下 3 条探测(都是纯查询,无副作用,秒级返回;无需拆子 agent): +主 agent 前台执行只读探测: ```bash -# 1. 探测本机全局是否装了 flow2spec -flow2spec --version 2>/dev/null || echo __F2S_NOT_INSTALLED__ -# 2. 查询 npm 上 latest 版本号(网络受限时可能失败,允许失败) -npm view @double-coding/flow2spec version 2>/dev/null || echo __F2S_NPM_UNREACHABLE__ -# 3. (备用)若第 1 步返回 __F2S_NOT_INSTALLED__,用来确认 npx 可用 -command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ +flow2spec version +flow2spec update --check ``` -**判定 3 分支**(按结果选一条,写入本轮上下文并影响步骤 2 与步骤 5 摘要): - -| 情况 | 判定条件 | 行动 | 步骤 2 命令默认形态 | -| --- | --- | --- | --- | -| **A. 已装且是 latest** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V === L` | **完全跳过升级**,本轮不派子 agent、不跑 `npm i -g` | **`flow2spec init `**(用全局) | -| **B. 已装但落后** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V !== L`(`V < L` 或 semver 不等) | **派独立子 agent 后台跑** `npm i -g @double-coding/flow2spec@latest`(fire-and-forget,不等待,不阻塞主流程);本轮步骤 2 仍用 `npx @latest` 保证本次拿到 latest 模板 | **`npx @double-coding/flow2spec@latest init `** | -| **C. 未装 or 版本无法确认** | 第 1 步命中 `__F2S_NOT_INSTALLED__`,或第 2 步命中 `__F2S_NPM_UNREACHABLE__` 且第 1 步也未拿到版本号 | 若 A 情况「已装 latest」不成立且**未装**:派独立子 agent 后台跑 `npm i -g ...@latest`(同 B);若第 2 步失败但第 1 步已装某版本:视作 B 且无法比对 latest,**不派**升级、仅提示「latest 未知,保守用 npx」 | **`npx @double-coding/flow2spec@latest init `** | +按输出记录 CLI Version、Core Version、Core Pinned、Template Version、Protocol Version,以及 npm 最新 Core/Template(CLI 对 Core 为精确 pin,两包联动发布): -**编排(必须)**: +| 情况 | 行动 | 步骤 2 默认命令 | +| --- | --- | --- | +| **A. Template 已是最新** | 若 CLI/Core 有更新,执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新)后幂等 init 刷新 Hook;随后**不得直接停止**,必须先做下方「**项目侧对齐检查**」,通过后才删除缓存并停止本技能 | `flow2spec init ` | +| **B. Template 有更新** | 执行 `flow2spec update --cli`(CLI 与配套 Core 一起到位),继续步骤 0 | `flow2spec init ` | +| **C. 未安装或版本未知** | 使用 latest CLI(自带 pin 的配套 Core),避免 npx 复用旧版缓存 | `npx --yes @latest init ` | -- **A 分支**:主 agent 直接跳过所有升级动作,**不派**子 agent;本轮步骤 2 命令首选 `flow2spec init`。 -- **B / C 分支**:若确需升级(未装或版本落后),派**独立子 agent** fire-and-forget 执行 `npm i -g @double-coding/flow2spec@latest`,**不等待完成**、**不阻塞**主流程;成败均不进入 SKILL 结论。该派子**强制**执行,**不受** `flow2spec.config.json.subAgent` 字段约束(全局 npm 装包不属业务拆分范畴)。 -- **写权**:子 agent 仅执行该 shell,**不**触碰 `.Knowledge` / `manifest-routing.json` / `index.md` 等任何项目文件;写权硬约束不变。 -- **探测失败兜底**:若 3 条探测全部失败(无 shell 权限、极端受限环境),按 C 分支处理并用 `npx @latest`;此时也可以直接放弃步骤 -1、把升级留给 `cli.js` 的 `maybeAutoUpdateGlobalInstall()` 收尾兜底。 +预检失败时允许回退 C,但不得把 Core Version 当作 Template Version。此步骤不强制创建子 agent,也不后台安装全局包。 -**与 cli.js 的关系**: +**项目侧对齐检查(A 分支停止前必须)**:`flow2spec version` / `update --check` 对比的是**本机包 vs npm 最新**,不反映**本项目 `.Knowledge` 是否已对齐当前包模板**(典型误判:刚升级完 CLI/Core 后首次在老项目跑本技能,包侧「均为最新」但项目侧模板仍是旧版,直接停止会跳过全部升级动作)。停止前逐项确认: -- `cli.js` 内 `maybeAutoUpdateGlobalInstall()` 是 `init` 收尾兜底逻辑,**与本步不冲突**:本步在前台 init 之前完成探测/派工,cli 那段在 init 收尾时再兜一次;两次都成功就是 no-op,第一次失败第二次还能补救。 +1. Read 项目侧 `.Knowledge/manifest-routing.json`:若 `version`(项目侧 Template Version)**低于**本机包 Template Version,或 `pkgRev` / `projectRev` 字段缺失 → 项目侧未对齐,**视同 Template 更新**:继续步骤 0 → 2(init)→ 2c 判定,不得停止; +2. 字段一致时再执行 `flow2spec kb check --strict`:若报 summary 质量 warning 或结构问题 → 转步骤 3a.7 / 3a.8 处理完再收尾; +3. 以上均通过才允许按 A 分支停止,并在摘要中写明「项目侧已对齐(version=X,strict 通过)」。 ### 步骤 0:版本判定与分流(必须,先于 init) @@ -165,10 +157,10 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 在目标项目根目录执行以下命令(**按步骤 -1 的分支结论选默认形态**): -1. **步骤 -1 判定为 A(已装且是 latest)**:直接用全局 CLI(**首选**): +1. **步骤 -1 判定为 A/B(本地 CLI/Core 可用)**:直接使用当前 CLI: - `flow2spec init ` -2. **步骤 -1 判定为 B/C(未装 / 落后 / latest 未知)**:拉 npm latest 跑(**保证本次拿到最新模板**): - - `npx @double-coding/flow2spec@latest init ` +2. **步骤 -1 判定为 C**:用 latest CLI(自带 pin 的配套 Core 与模板): + - `npx --yes @latest init ` 3. 覆盖重置时: - 在上述命令末尾追加 `--reset-knowledge` 4. 用户显式要求切换模板语言时: @@ -177,7 +169,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ > `` 示例:`cursor claude codex`。 -> **辅助命令(用户可自查)**:`flow2spec --version` 看当前全局版本;`flow2spec update` 触发 CLI 内置的自更新。这两条**不**替代本 SKILL 的完整流程——它们只是「让全局 CLI 保鲜」,主题层对齐仍须走本 SKILL 步骤 2 及以后。 +> **辅助命令(用户可自查)**:`flow2spec version` 查看五维版本;`flow2spec update --check` 检查更新,`flow2spec update --cli` 整体更新(CLI 与配套 Core 联动,`--core` 为其等价别名)。这些命令不替代 Template Version 变化后的本技能完整流程。 **步骤 2 完成后**:立刻执行上文 **「init 与技能自更新」**:重读 **`skills/f2s-kb-upgrade/SKILL.md`**;若有更新则**按新版字面从步骤 2c 起重跑**(**不再次 init**;避免用旧版 SKILL 做后续校验)。 @@ -243,7 +235,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ - `includeAny` 词数超过 **12 个**; - topic 正文包含超过 **3 个不相干职责域**的二级标题; - 该 topic 同时被多种不相干任务类型频繁命中(可从 `taskToTopicRules` 和 matcher 词宽度判断)。 -7. **旧 topic frontmatter 自动补齐**:完整流程中必须由 agent 自行执行 `flow2spec kb build --fix-topics`(或等价内部能力),为缺少 frontmatter / `revision` 的存量 topic 补 `id`、`revision`、`summary`,并按 `manifest-routing.json` 补 `dependsOn` / `primary` / `confidence` / `tags`。随后执行 `flow2spec kb check --strict`;若 strict 失败,停止并在摘要中列出具体 topic / reason。不得要求用户手动逐个 topic 添加头部。 +7. **旧 topic frontmatter 自动补齐**:完整流程中必须由 agent 自行执行 `flow2spec kb build --fix-topics`(或等价内部能力),为缺少 frontmatter / `revision` 的存量 topic 补 `id`、`revision`、`summary`,并按 `manifest-routing.json` 补 `dependsOn` / `primary` / `confidence` / `tags`。随后执行 `flow2spec kb check --strict`;若失败项为 **summary 质量 warning**(缺失 / 占位 / 超长),转第 8 条补写;其余失败停止并在摘要中列出具体 topic / reason。不得要求用户手动逐个 topic 添加头部。 +8. **summary 初筛锚补写(必须,agent 才能写出语义摘要)**:对 `kb check --strict` 报出的每个 summary 质量 warning,逐个 Read 对应 `.Knowledge/topics/.md` 正文,按 `f2s-topic-authoring`「初筛召回规范」补写 frontmatter `summary`(职责 + 用户会问的核心名词,软 30 字 / 硬 40 字);全部补完后执行 `flow2spec kb build` 同步进 `rule.summary`,再跑 `flow2spec kb check --strict` 直至无 summary warning。禁止用占位文案(`(路由摘要)` / TODO)敷衍通过。 ### 步骤 3b:`index.md` 融合与 `template/index.template.md`(必须执行) @@ -300,7 +293,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 输出以下信息: -- **步骤 -1 全局版本预检**:分支结论(`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级) / C 未装或 latest 未知(已派或提示)`)+ 当前全局版本 + npm latest 版本(若拿到) +- **步骤 -1 全局版本预检**:分支结论(`A 已装且是 latest(需附项目侧对齐结论) / B 已装但落后(已派子 agent 后台升级) / C 未装或 latest 未知(已派或提示)`)+ 当前全局版本 + npm latest 版本(若拿到) +- **项目侧对齐检查**(A 分支时必填):`已对齐(version=X,strict 通过,已停止)` / `未对齐(version X < 包 Y,已转完整流程)` / `strict 报 warning(已转 3a.7/3a.8)` - 执行命令(含 agent 与是否 reset) - 是否成功 - **`projectRev` 判定**:`projectRev` X vs `pkgRev` Y → 快速路径 / 完整流程 / 字段缺失走兜底(步骤 2c) @@ -319,8 +313,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ ```markdown ## f2s-kb-upgrade 执行结果 -- **步骤 -1 全局版本预检**:`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=``,latest=`` -- 本技能内代跑命令:`<实际执行的 flow2spec init ... 或 npx @latest init ...>` +- **步骤 -1 全局版本预检**:`A 已装且是 latest(项目侧对齐:<结论>) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=``,latest=`` +- 本技能内代跑命令:`<实际执行的 flow2spec init ... 或显式 latest CLI/Core 组合 init ...>` - init 模式:`增量` / `覆盖重置(--reset-knowledge)` - 执行结果:`成功` / `失败` - **主题层判定**:`projectRev=` vs `pkgRev=` → `快速路径(已跳过 3/3a/3b)` / `完整流程` / `字段缺失走兜底` @@ -331,6 +325,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ - **index(快照 + 融合)**:`快照已复制` / `index.md 已融合` / `快速路径下未执行` / `待处理(见备注)` - **topicMetadata(存量审计)**:`已补齐` / `待用户确认` / `快速路径下未执行`;列出新增 / 修正 / 删除的 topicId - **topic frontmatter**:`已自动补齐 N 个` / `已完整无需补齐` / `strict 校验失败` / `快速路径下未执行` +- **summary 初筛锚**:`已补写 N 个(已 kb build 同步 rule.summary)` / `已合规无需补写` / `快速路径下未执行` - **f2s-kb-upgrade SKILL**:`init 后无变化` / `已按新版从 2c 起重跑 N 轮(不再次 init)` / `快速路径下跳过该闭环` / `待确认` - **`projectRev` 回写**:`已写入项目 manifest(值=pkgRev)` / `快速路径下未执行` / `pkgRev=null 未动` - manifest-routing / matchers 分片:`已与模板对齐` / `已是最新` / `reset 覆盖` @@ -351,7 +346,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ ## 完成后自检 -1. 是否已做 **步骤 -1**:在进入步骤 0 前**已顺序前台执行 3 条探测**(`flow2spec --version` / `npm view ... version` / `npx` 可用性),并按 A/B/C 分支得出结论;仅在 B/C 时才**派独立子 agent**后台跑 `npm i -g @double-coding/flow2spec@latest`(不等待),A 分支**未派**任何升级动作;步骤 2 命令默认形态是否随分支选定(A→`flow2spec init`,B/C→`npx @latest init`);摘要中已写清分支与版本对比。 +1. 是否已做 **步骤 -1**:执行 `flow2spec version` 与 `flow2spec update --check`,记录 CLI/Core/Core Pinned/Template/Protocol;有更新时是否执行 `flow2spec update --cli` 联动刷新 CLI 与配套 Core,Template 更新是否按 A/B/C 选择当前 CLI 或 latest CLI;**A 分支停止前是否完成「项目侧对齐检查」**(manifest `version`/`pkgRev` 对比 + `kb check --strict`),未对齐时是否已转完整流程而非直接停止。 2. 是否已做 **步骤 0**:V1 已停止执行并告知用户处理方式、**现行库(V2+)** 正常进入 `init` 流程。 3. 是否在 **步骤 2 开始前** 记录了项目侧 `projectRev`(`projectRev`),并在 **步骤 2 的 `init` 之后** 重读 `pkgRev`、执行 **步骤 2c** 判定。 4. 是否在 **步骤 2 的 `init` 之后**重读过 **`f2s-kb-upgrade/SKILL.md`**:完整流程下有变化必须**按新版字面从步骤 2c 起重跑**(**不再次 init**);快速路径下可跳过该闭环(见「init 与技能自更新」「快速路径例外」)。 @@ -359,7 +354,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 6. 是否明确标注增量 or reset 模式。 7. **完整流程时**:是否已处理旧主题文件清理与 `index/manifest` 引用修复(步骤 3)。 8. **完整流程时**:是否已执行 **步骤 3a**:审计 `topicMetadata`,确保无孤儿 key / 非法 primary / 非法 confidence;缺失旧主题已按证据补 `inferred` 或列为待确认。 -9. **完整流程时**:是否已执行 `flow2spec kb build --fix-topics` 或等价内部能力,并随后执行 `flow2spec kb check --strict`,确保存量 topic 已具备 `revision`。 +9. **完整流程时**:是否已执行 `flow2spec kb build --fix-topics` 或等价内部能力,并随后执行 `flow2spec kb check --strict`,确保存量 topic 已具备 `revision`;summary 质量 warning 是否已按步骤 3a.8 补写清零并 `kb build` 同步 `rule.summary`。 10. **完整流程时**:是否已执行 **步骤 3b**:**融合** `index.md`(**主题一览**节起至命中与执行前为项目维护区,其余同包版),并核对 `topicPaths`;**完整流程末尾**是否已**回写** 项目侧 `projectRev = pkgRev`(`pkgRev=null` 则保留原值)。 11. **快速路径时**:步骤 3 / 3a / 3b 是否真的跳过(未做无关扫描),摘要中明确标注「快速路径下未执行」。 12. 是否输出了 manifest 与关键路径校验结果。 diff --git a/.codex/topics/f2s-flow2spec-unified-entry.md b/.codex/topics/f2s-flow2spec-unified-entry.md index dd99d27..6928647 100644 --- a/.codex/topics/f2s-flow2spec-unified-entry.md +++ b/.codex/topics/f2s-flow2spec-unified-entry.md @@ -24,7 +24,7 @@ ## 读取顺序(必须) -1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;按需根据 `matcherPath` 读取 matcher 分片获取 `includeAny` 关键词;无法命中时进入补召回阶段。 +1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;初筛证据为每条规则的 `task` 名与 `summary`(一句话意图摘要);按需根据 `matcherPath` 读取 matcher 分片获取词表(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词;否决门恒胜,优先于 `task` 精确命中,详见 `topics/kb-routing-summary.md`);无法命中时进入补召回阶段。 - 若命中主题在 `topicDependencies` 中存在依赖,先读依赖主题,再读主主题。 - 路由清单仅通过 `f2s-*` 技能流程维护,不依赖额外 CLI 子命令。 2. `.Knowledge/index.md` 按需读取,仅用于确认主题语义与边界。 @@ -32,9 +32,9 @@ 4. 若需要背景,再读 `.Knowledge/stock-docs/.md`。 5. 仅在前四步不足时下钻业务源码。 6. 命中后必须执行 `match -> expand -> verify -> act`: - - `match`:先取主候选; + - `match`:以规则的 `task` 名与 `summary` 做语义匹配取主候选;`summary` 与 `includeAny` 均为语义锚而非字面白名单,允许近义命中; - `expand`:展开 `topicDependencies`,并保留次高候选做补充校验; - - `verify`:执行前做缺口检查(关键主题/边界/上下文是否缺失); + - `verify`:执行前做缺口检查(关键主题/边界/上下文是否缺失);**若命中主题正文未覆盖用户问句的核心名词,必须并读次高候选的 `summary` 与 matcher 分片再定,不得直接作答**; - `act`:仅在置信度足够时执行;低置信度必须先澄清。 7. 仅在以下条件之一成立时,允许执行跨 matcher 全量补检索(top-k): - `taskToTopicRules` 无命中; @@ -50,11 +50,12 @@ ## 机读事实源口径(规则层) - `taskToTopicRules`:任务路由第一优先级。 +- `taskToTopicRules[].summary`:初筛召回字段(一句话意图摘要),由 `flow2spec kb build` 从 topic frontmatter `summary` 机械同步,不手写 manifest 侧;作语义锚参与 match 初筛。 - `taskToTopicRules[].matcherPath`:匹配词分片直链路径,按需读取单个 matcher 文件。 - `taskToTopicRules[].matcherId`:matcher 的稳定标识,需与 matcher 分片内 `id` 一致。 - `topicDependencies`:主主题命中后先加载依赖主题。 - `topicMetadata`:主题治理元数据,只影响阅读预期,不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性;执行强制性始终以 `AGENTS.md`、rules、skills 与 topic 正文中的明确要求为准。读到 `topicMetadata[topicId].primary` / `tags` 时:`config` 关注配置项、开关、默认值、初始化参数;`policy` 优先检查正文中的必须/禁止/门禁/流程约束;`feature` 作为已落地业务/产品能力背景;`module` 作为目录、包、模块边界与工程结构背景。`confidence` 仅允许 `manual` / `inferred`;无明确分类证据时不写 metadata。 -- `matcherPath(includeAny)`:任务关键词匹配词表。 +- `matcherPath(includeAny/includeAll/excludeAny/excludeAll)`:任务关键词匹配词表(前两者为资格门,后两者为否决门;详见 `topics/kb-routing-summary.md`)。 - `fallbackTopic`:任务与关键词都未命中时必须读取,但仅作低置信度兜底,不是最终执行依据。 - `.Knowledge/manifest-routing.json + matcherPath 分片文件` 是机读事实源(关键词仅在 `matchers/*.json`)。 - `.Knowledge/index.md` 不是机读事实源,仅作人读导航与语义边界校验。 @@ -64,7 +65,7 @@ | 情况 | 对策 | | --- | --- | -| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` 覆盖用户常用说法。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | +| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` / `includeAll` 覆盖用户常用说法,必要时用 `excludeAny` 排除误路由。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | | **1b 命中了但上下文不够** | 先 `expand`(`topicDependencies` + 次高候选),再 `verify` 点名缺哪份 `stock-docs`/`req-docs` 或哪段 topic;仍不足则 **向用户要文档或路径**,不要无门槛跨 matcher 全量补检索。**Agent 若需下钻源码**:须先对用户做**可见的缺口说明**(已读 KB、缺什么、拟读哪 1~2 个文件),见 **`f2s-knowledge-preflight`**「缺口闸门」;**禁止**无说明地连续 `Grep`/乱序探源。 | | **2 库里没有对应文档** | 一次读完 routing + 已命中 matcher + 相关 topic 后,在回复中 **明确承认知识库无覆盖**,再选:下钻业务代码 / 请用户补充 `req-docs` 或 PRD。**禁止**用反复读清单假装「再找一遍就会有」。**下钻源码前**同样须满足 **`f2s-knowledge-preflight`**「缺口闸门」的可见说明。 | | **2a 反复读清单耗 token** | **同一任务线内** `manifest-routing.json` 视为稳定快照:再次全文读取须说明理由(例如用户声明已通过 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 等更新路由或知识、或**手动编辑**了 manifest/matcher)。**勿将**仅执行 **`flow2spec init`** 等同于「业务知识库已更新」:`init` 以配置根落盘、目录补齐与包级路由结构对齐为主;**stock-docs / req-docs、topics 路由摘要、matchers 词条**由 **`f2s-*` 技能流程**维护;`init` 会把规则写入配置根 **`rules/*`**(或等价扩展名),并为 Codex 写入 **`.codex/topics/*.md`**。只读 **当前规则对应的单个** `matcherPath`;不要为枚举而遍历整个 `matchers/` 目录。`index.md` 仅在需核对主题语义时打开,禁止与 manifest 交替「刷清单」。 | @@ -92,9 +93,9 @@ **规则层双保险**(与脚本缓存互为备份): 1. 读 `flow2spec.config.json` → 若 `updateCheck.enabled` 不为 `true`,跳过,不做任何提示。 -2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日(`new Date(checkedAt).toDateString() === new Date().toDateString()`),不重复查 npm;但若 `needsUpgrade=true` 或 `latestNpm > manifestVersion`,本会话首次回复用户时仍须转告升级提示,并可**直接替用户执行** `flow2spec init <已初始化的 agents>` 完成更新——init 后读 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` 相等(非主题版本更新)即完成并删除该缓存;不等(含主题层变更)再执行 `f2s-kb-upgrade` 完整流程(可从其步骤 2c 起,不重复 init);若当前 `.Knowledge/manifest-routing.json.version` 已不低于 `latestNpm`,删除该缓存并不再提示。 +2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日,不重复查 npm;`coreUpdateAvailable=true` 时可执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新)。若 `templateUpdateAvailable=false`,随后只执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook,删除缓存,不进入 `f2s-kb-upgrade`;若 `templateUpdateAvailable=true`,更新后执行 init,再以 `projectRev` / `pkgRev` 判定快速路径或完整流程。`.Knowledge/manifest-routing.json.version` 表示 Template Version,禁止与 Core Version 直接比较。 3. 上述两步均未跳过时:执行当前 agent 配置根下的更新检测脚本(Claude:`node .claude/hooks/f2s-update-check.js`;Cursor:`node .cursor/hooks/f2s-update-check.js`;Codex:`node .codex/hooks/f2s-update-check.js`),解析标准输出的 JSON: - - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 执行(非主题版本更新可直接代跑 `flow2spec init`;主题层变更再执行 `f2s-kb-upgrade` skill)。 + - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 分别处理 Core-only 与 Template 更新。 - 无输出或解析失败:静默,不提示。 4. 以上步骤出现任何错误,静默跳过,不影响正常对话。 diff --git a/.codex/topics/f2s-kb-feedback-closing.md b/.codex/topics/f2s-kb-feedback-closing.md index 64065cd..ed956db 100644 --- a/.codex/topics/f2s-kb-feedback-closing.md +++ b/.codex/topics/f2s-kb-feedback-closing.md @@ -104,6 +104,12 @@ 这样能避免"已有 topic 还建议 add"的误判。 +## 路由 miss 反哺(强制) + +- **判定**:本轮答案的核心事实由某 topic 提供,但该 topic **不是初筛主命中**(靠全量补检索、次高候选并读或用户点名才找到)→ 计为一次路由 miss。 +- **动作**:在四 case 收口之外,把本轮用户的原始问法回填该 topic 的召回锚——按 `f2s-topic-authoring`「初筛召回规范」补 frontmatter `summary` 的核心名词或 matcher `includeAny` 的单概念词,并跑 `flow2spec kb build` 同步 rule.summary。此项是直接落盘的最小修复,不依赖用户再跑 distill。 +- 即使收口走 case 4(知识库已覆盖),只要命中路径是 miss 后补救,仍须执行本反哺,并在收口块后追加一行:`已回填路由: 的 summary/includeAny 补「<词>」`。 + ## 输出格式 - case 1~3:输出一个 Markdown 引用块,依次写 `f2s-kb-distill` 命令 + 一行空行 + **本轮将入库**概要(一句话,见上文「概要要求」)。 diff --git a/.codex/topics/f2s-topic-authoring.md b/.codex/topics/f2s-topic-authoring.md index 665f88d..1a62ed8 100644 --- a/.codex/topics/f2s-topic-authoring.md +++ b/.codex/topics/f2s-topic-authoring.md @@ -28,13 +28,49 @@ 每个 topic 至少包含: 1. **标题与一句话意图**(一行写清"该 topic 解决什么"); -2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` 语义一致); +2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` / `includeAll` 语义一致); 3. **核心规则 / 流程**(可执行知识;步骤须可由 Agent 复现); 4. **依赖声明**(若 `topicDependencies` 中存在依赖项,正文须显式写一句「执行前须先读依赖主题 ``」,参考 `topics/f2s-req-plan.md` 首段写法); 5. **边界与禁止项**(避免膨胀到隔壁 topic); 6. **长文背景 / 详细资料引用**(如需承载业务背景):只列 `.Knowledge/stock-docs/*_终稿.md` 的可点击 Markdown 链接(1–3 条即可);**禁止**直接列 `.Knowledge/req-docs/*` 作为长文背景来源;无对应终稿时**先生成终稿**再回填此小节。 -## 3. topicMetadata 判定准则 +### frontmatter `summary` 与 `includeAny` 创作规范(初筛召回,硬约束) + +topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest-routing.taskToTopicRules[].summary`,是 Agent 初筛的常驻语义锚——写作质量直接决定该 topic 能否被自然问法命中: + +- **`summary`**:一句话写清「职责 + 用户会问的核心名词」,覆盖 topic 的全部职责域(如同时承担“职责边界”与“资料入口”,两者都要出现);软上限 30 字(英文 15 词),硬上限 40 字 / 20 词(`kb check` 校验);禁止 `(路由摘要)`、`TODO` 等占位写法。 +- **`includeAny`**:单概念核心词优先(自然问法中会独立出现的最小词,如「原型」「流程图」而非「原型位置」「业务流程图」);复合词仅作补充;从用户口述沉淀时须补 1–2 个“日后自然问法”词,不只抄口述原文。 +- **自测(落盘前必做)**:模拟 2–3 个用户自然问句(凭空想“用户会怎么问”,不看词表),检查每句至少命中 `summary` 或 `includeAny` 之一;不命中则回改。 +- **`taskToTopicRules[].task`**:保持稳定 id 语义(kb 引擎按 `task` 合并),不塞长短语;召回语义由 `summary` 承载,manifest 侧由 `kb build` 生成,不手写。 + +## 3. matcher 分片字段语义 + +matcher 分片(`.Knowledge/matchers/.json`)有 4 个字段参与 `routing.match()` 判定,按「否决门 → 资格门」两级: + +- **资格门**(至少满足其一即候选): + - `includeAny`:任一短语命中(OR)——最常用 + - `includeAll`:所有短语全部命中(AND)——表达组合词/上下文约束 +- **否决门**(任一门命中即整条规则出局,**恒胜,优先于 `task` 精确命中**): + - `excludeAny`:任一短语命中(OR) + - `excludeAll`:所有短语全部命中(AND);半命中不否决 + +写作准则: + +1. **默认只写 `includeAny`**:大部分主题不需要否决词/AND 词;泛化的单概念核心词交给 `includeAny` 即可。 +2. **用 `excludeAny` 排除误路由**:当同一批 `includeAny` 词在**另一场景**下反复被错命中(如 "部署" 词被 "预演部署" 误命中),用 `excludeAny: ["预演", "preview"]` 精确排除,**保留召回率**——比删触发词代价小得多。 +3. **用 `includeAll` 表达组合词**:仅当**两个词单独都太泛、但同时出现才有意义**时使用(如 "数据 + 迁移");否则拆成两条 `includeAny` 更好。 +4. **用 `excludeAll` 表达"仅当同时否定"**:仅当"任一词单独出现都不应否决,但两个词同时出现明确不属于本任务域"时使用(如 "归档 + 下线");这是罕见场景,大多数否决需求用 `excludeAny` 就够。 +5. **否决优先于 `task` 精确命中**:即使调用方显式传入 `task: "deploy"`,只要 `excludeAny` 命中,该规则仍然出局——排除词表达的是「此请求不属于该任务域」,语义强于精确命中。**若你不想让 `task` 参数被否决,不要给这条 rule 写 `excludeAny`**。 +6. **打分池合并**:`includeAll` 命中的短语与 `includeAny` 命中一起进入打分池,`score` 取最高短语分,不改变 `confidence` 分档规则(exact task=high、≥30=medium、<30=low)。 + +阈值提示: + +- `includeAny` 词表超过 12 个通常是主题过宽信号(见第 6 节拆分策略);若通过 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估。 +- 排除词无阈值约束,但写超过 5 条通常意味着资格词已过泛,应回头收紧资格门。 + +实现:`packages/core/lib/routing.js:match()`;契约测试:`scripts/test-routing-semantics.js`(10 组用例覆盖 4 字段判定门与 exact-task 交互)。 + +## 4. topicMetadata 判定准则 `topicMetadata` 是治理元数据,只影响盘点、审计和阅读预期;不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性。执行强制性以 `AGENTS.md`、rules、skills 与 topic 正文明确要求为准。 @@ -57,7 +93,7 @@ 禁止:为了分类创建、重命名、拆分 topic;在 topic markdown 正文或 `index.md` 中重复写分类块。 -## 4. topicDependencies 判定准则 +## 5. topicDependencies 判定准则 设当前主题为 A、候选依赖为 B。**四问命中任一即声明 `A → B`**: @@ -77,14 +113,14 @@ **判定时机**:终稿与新 / 改 topic 落盘后,扫正文中**反引号引用的其他 topic id 与规则文件名**,逐个套四问;命中即写入 `manifest-routing.topicDependencies`,**并在新 topic 正文显式写依赖声明**(见骨架第 4 条)。 -## 5. 大功能拆分策略 +## 6. 大功能拆分策略 当一个业务功能体量较大时,推荐「主 topic + 子 topic」结构,而非单个大 topic。 **何时拆分(软约束,满足任一评估是否需拆)**: - 对应 stock-doc 超过 **300–500 行**:建议评估拆分,不强制阻断; -- matcher `includeAny` 超过 **12 个**:主题过宽信号; +- matcher `includeAny` 超过 **12 个**:主题过宽信号(若用 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估,详见第 3 节); - topic 正文包含超过 **3 个不相干职责域**的二级标题; - `f2s-kb-upgrade` 审计时发现同一 topic 被多种不相干任务类型反复命中。 @@ -96,10 +132,10 @@ **不要做的事**: -- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 4 节反向排除); +- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 5 节反向排除); - 不为拆分而强行制造子 topic,若子模块本身不会被独立路由命中,不必建 topic。 -## 6. rule 是否需新建对应 topic +## 7. rule 是否需新建对应 topic 判据:**该 rule 是否会作为用户任务路由命中**。 @@ -108,7 +144,7 @@ 误区:「重要的规则就该有 topic」——重要不等于"用户路由命中";让消费方 SKILL 在正文里直接 `Read rules/.*` 全文即可,无需走 manifest 路由。 -## 7. 写盘权属(指针) +## 8. 写盘权属(指针) `manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/topics/*.md` 的写权约束**以 `f2s-flow2spec-unified-entry` 与各 SKILL 内「写权硬约束」为准**,本条不复述;遇分歧以统一入口与对应 SKILL 为准。 @@ -117,7 +153,7 @@ - 在未读本条的情况下新增 / 修改 topic 或 `topicDependencies`。 - 为补分类单独创建、重命名或拆分 topic。 - 在 topic 正文或 `index.md` 中写 `## 概念分类` 等 metadata 副本。 -- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 4 条)。 +- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 7 条)。 - 用 `topicDependencies` 表达"信息相关"(应通过 `index.md` 语义边界 + matcher 关键词补召回,而非依赖边)。 - 在 `topicDependencies` 中写传递冗余边或形成环。 - **在 topic 的「长文背景 / 详细资料 / 相关资料 / 长文来源 / 参考文档」等指向长文源的整节引用槽位里,列出 `.Knowledge/req-docs/*`**(含澄清 / 技术方案 / SQL / PRD)。此类槽位只允许指向 `.Knowledge/stock-docs/*_终稿.md`;无终稿时须先生成再回填。短句/佐证式的偶发点引不受此约束。 \ No newline at end of file diff --git a/.cursor/hooks/f2s-update-check.js b/.cursor/hooks/f2s-update-check.js index ad5436f..208c7b8 100644 --- a/.cursor/hooks/f2s-update-check.js +++ b/.cursor/hooks/f2s-update-check.js @@ -1,189 +1,156 @@ #!/usr/bin/env node 'use strict'; -/** - * flow2spec SessionStart hook — 每天第一次对话时检查版本更新。 - * 比较本地知识库 manifest-routing.json 的 version 与 npm 最新版本: - * - 一致或本地更新 → 静默退出 - * - 落后 → 向 Agent 上下文注入提示(agent 可直接代跑 flow2spec init;主题层变更再走 f2s-kb-upgrade) - * 已检查过且无需升级则静默;已检查且仍需升级时,每个新会话继续注入提醒。 - * 由 flow2spec init 写入对应 agent 的 hooks/f2s-update-check.js。 - */ -const fs = require('fs'); + +const fs = require('fs'); const path = require('path'); const { execFileSync } = require('child_process'); const MANIFEST_PATH = path.join(process.cwd(), '.Knowledge', 'manifest-routing.json'); -const CACHE_DIR = path.join(process.cwd(), '.Knowledge'); -const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json'); +const CACHE_FILE = path.join(process.cwd(), '.Knowledge', 'update-check.json'); const PACKAGE_NAME_PLACEHOLDER = '__FLOW2SPEC_' + 'PACKAGE_NAME__'; const PACKAGE_NAME = '@double-coding/flow2spec-core'; +const GENERATED_CORE_VERSION = '3.7.2'; +const GENERATED_TEMPLATE_VERSION = '3.6.2'; -// ── 缓存 ──────────────────────────────────────────────────────────────────── - -function readCache() { - if (!fs.existsSync(CACHE_FILE)) return null; - try { - const d = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); - if (!d || typeof d !== 'object') return null; - const checkedAt = Number(d.checkedAt || 0); - if (!checkedAt) return null; - if (new Date(checkedAt).toDateString() !== new Date().toDateString()) return null; - return d; - } catch (_) { return null; } +function readJson(file) { + try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { return null; } } -function getProjectName() { - try { - const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); - if (pkg && pkg.name) return String(pkg.name); - } catch (_) {} - return path.basename(process.cwd()); +function parseVer(value) { + return String(value || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((part) => { + const number = Number.parseInt(part, 10); + return Number.isFinite(number) ? number : 0; + }); } -function buildNotice(latestNpm, manifestVersion) { - const userMsg = `📦 [flow2spec] 当前项目「${getProjectName()}」知识库版本 v${manifestVersion},低于最新包版本 v${latestNpm}。我可以直接代跑 flow2spec init 完成更新;若本次更新包含主题层变更,再走 f2s-kb-upgrade 完整流程。`; - return [ - `[flow2spec/agent-instruction] 你必须在本轮回复开头将下列升级提示原文转告用户,不得省略、不得仅在内部判断后跳过:`, - ``, - userMsg, - ``, - `[flow2spec/agent-instruction] 转告后可直接替用户执行 \`flow2spec init <已初始化的 agents,如 claude cursor codex>\` 完成更新,无需用户手动执行 f2s-kb-upgrade。init 成功后读取 .Knowledge/manifest-routing.json:若 projectRev 与 pkgRev 相等(非主题版本更新),更新已完成,删除 .Knowledge/update-check.json;若两者不等(包含主题层变更),继续执行 f2s-kb-upgrade skill 完整流程(可从其步骤 2c 起,不必重复 init)。`, - ].join('\n'); +function cmpVer(left, right) { + const a = parseVer(left), b = parseVer(right); + for (let index = 0; index < 3; index += 1) { + const difference = (a[index] || 0) - (b[index] || 0); + if (difference !== 0) return difference; + } + return 0; } -function emitNotice(notice) { - process.stdout.write( - JSON.stringify({ - additional_context: notice, - hookSpecificOutput: { - hookEventName: 'SessionStart', - additionalContext: notice, - }, - }) + '\n' - ); +function getProjectName() { + return readJson(path.join(process.cwd(), 'package.json'))?.name || path.basename(process.cwd()); } -function writeCache(latestNpm, manifestVersion) { - try { - const needsUpgrade = cmpVer(manifestVersion, latestNpm) < 0; - fs.writeFileSync( - CACHE_FILE, - `${JSON.stringify({ - latestNpm, - manifestVersion, - needsUpgrade, - notice: needsUpgrade ? buildNotice(latestNpm, manifestVersion) : '', - checkedAt: Date.now(), - }, null, 2)}\n`, - 'utf8' - ); - } catch (_) {} +function getManifestVersion() { + return readJson(MANIFEST_PATH)?.version || null; } -function deleteCache() { - try { - if (fs.existsSync(CACHE_FILE)) fs.unlinkSync(CACHE_FILE); - } catch (_) {} +function getPackageName() { + return PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER + ? PACKAGE_NAME + : '@double-coding/flow2spec-core'; } -// ── 版本比较 ───────────────────────────────────────────────────────────────── - -function parseVer(v) { - return String(v || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((p) => { - const n = Number.parseInt(p, 10); - return Number.isFinite(n) ? n : 0; - }); +function isEnabled() { + const config = readJson(path.join(process.cwd(), 'flow2spec.config.json')); + return config?.updateCheck?.enabled !== false; } -/** a < b → 负数;a === b → 0;a > b → 正数 */ -function cmpVer(a, b) { - const av = parseVer(a), bv = parseVer(b); - for (let i = 0; i < 3; i++) { - const d = (av[i] || 0) - (bv[i] || 0); - if (d !== 0) return d; - } - return 0; +function readCache() { + const cache = readJson(CACHE_FILE); + if (!cache?.checkedAt) return null; + return new Date(cache.checkedAt).toDateString() === new Date().toDateString() ? cache : null; } -// ── 读取 ───────────────────────────────────────────────────────────────────── +function queryLatestMetadata(packageName) { + const output = execFileSync( + 'npm', + ['view', packageName, 'version', 'templateVersion', '--json', '--registry=https://registry.npmjs.org'], + { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }, + ); + const metadata = JSON.parse(output); + const latestCoreVersion = typeof metadata === 'string' ? metadata : metadata.version; + const latestTemplateVersion = typeof metadata === 'string' + ? metadata + : metadata.templateVersion || metadata.version; + return { latestCoreVersion, latestTemplateVersion }; +} -function getManifestVersion() { - if (!fs.existsSync(MANIFEST_PATH)) return null; - try { - return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8')).version || null; - } catch (_) { return null; } +function updateState(metadata, manifestVersion) { + const coreUpdateAvailable = cmpVer(GENERATED_CORE_VERSION, metadata.latestCoreVersion) < 0; + const templateUpdateAvailable = cmpVer(manifestVersion, metadata.latestTemplateVersion) < 0; + return { + packageName: getPackageName(), + generatedByCoreVersion: GENERATED_CORE_VERSION, + generatedTemplateVersion: GENERATED_TEMPLATE_VERSION, + manifestVersion, + ...metadata, + coreUpdateAvailable, + templateUpdateAvailable, + needsUpgrade: coreUpdateAvailable || templateUpdateAvailable, + }; } -function getPackageName() { - if (PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER) { - return PACKAGE_NAME; +function buildNotice(state) { + const project = getProjectName(); + const versionSummary = `Core v${state.generatedByCoreVersion} -> v${state.latestCoreVersion},Template v${state.manifestVersion} -> v${state.latestTemplateVersion}`; + if (state.templateUpdateAvailable) { + return [ + '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', + '', + `📦 [flow2spec] 当前项目「${project}」有模板更新(${versionSummary})。`, + '', + '[flow2spec/agent-instruction] 可先执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新),再执行 `flow2spec init <已初始化的 agents>`。init 后读取 `.Knowledge/manifest-routing.json`:projectRev 与 pkgRev 相等则更新完成并删除 `.Knowledge/update-check.json`;不等时再执行 f2s-kb-upgrade(可从步骤 2c 继续)。', + ].join('\n'); } - return '@double-coding/flow2spec'; + return [ + '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', + '', + `📦 [flow2spec] 当前项目「${project}」仅有 Core 程序更新(${versionSummary}),模板版本未变化。`, + '', + '[flow2spec/agent-instruction] 可执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新),随后执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook。Template Version 未变化,不进入 f2s-kb-upgrade;完成后删除 `.Knowledge/update-check.json`。', + ].join('\n'); } -function queryNpmLatest(pkgName) { - return execFileSync('npm', ['view', pkgName, 'version'], { - encoding: 'utf8', - timeout: 5000, - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); +function emitNotice(notice) { + process.stdout.write(JSON.stringify({ + additional_context: notice, + hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: notice }, + }) + '\n'); } -// ── 配置开关 ────────────────────────────────────────────────────────────────── - -function isEnabled() { +function writeCache(state) { try { - const cfg = JSON.parse(fs.readFileSync( - path.join(process.cwd(), 'flow2spec.config.json'), 'utf8' - )); - const uc = cfg && cfg.updateCheck; - if (uc && typeof uc.enabled === 'boolean') return uc.enabled; - return true; - } catch (_) { return true; } + fs.writeFileSync(CACHE_FILE, `${JSON.stringify({ + ...state, + latestNpm: state.latestTemplateVersion, + notice: state.needsUpgrade ? buildNotice(state) : '', + checkedAt: Date.now(), + }, null, 2)}\n`, 'utf8'); + } catch (_) {} } -// ── 主流程 ──────────────────────────────────────────────────────────────────── +function deleteCache() { + try { fs.rmSync(CACHE_FILE, { force: true }); } catch (_) {} +} function main() { - if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return; - if (!isEnabled()) return; - const cache = readCache(); - if (cache) { - // 今天已检查过则不重复查 npm;若缓存显示仍需升级,每个新会话继续提醒。 - const needsUpgrade = cache.needsUpgrade === true || - cmpVer(cache.manifestVersion, cache.latestNpm) < 0; - if (needsUpgrade) { - const currentManifestVersion = getManifestVersion(); - if (currentManifestVersion && cache.latestNpm && - cmpVer(currentManifestVersion, cache.latestNpm) >= 0) { - deleteCache(); - return; - } - // SessionStart 进入新会话:缓存命中且仍需升级,直接 emit。 - const notice = buildNotice(cache.latestNpm, cache.manifestVersion); - emitNotice(notice); - } - return; - } - + if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || !isEnabled()) return; const manifestVersion = getManifestVersion(); - if (!manifestVersion) return; // 无知识库,跳过 - - let latestNpm; - try { - const pkgName = getPackageName(); - latestNpm = queryNpmLatest(pkgName); - } catch (_) { - return; // 网络不通,静默退出,不写缓存(下次还会重试) + if (!manifestVersion) return; + + const cached = readCache(); + if (cached) { + const state = updateState({ + latestCoreVersion: cached.latestCoreVersion, + latestTemplateVersion: cached.latestTemplateVersion || cached.latestNpm, + }, manifestVersion); + if (!state.needsUpgrade) deleteCache(); + else emitNotice(buildNotice(state)); + return; } - // 写缓存(无论是否需要升级,今天不再重复检查) - writeCache(latestNpm, manifestVersion); - - if (cmpVer(manifestVersion, latestNpm) >= 0) return; // 已是最新 - - const notice = buildNotice(latestNpm, manifestVersion); - emitNotice(notice); + let metadata; + try { metadata = queryLatestMetadata(getPackageName()); } catch (_) { return; } + if (!metadata.latestCoreVersion || !metadata.latestTemplateVersion) return; + const state = updateState(metadata, manifestVersion); + writeCache(state); + if (state.needsUpgrade) emitNotice(buildNotice(state)); } main(); diff --git a/.cursor/rules/f2s-flow2spec-unified-entry.mdc b/.cursor/rules/f2s-flow2spec-unified-entry.mdc index f28b98a..f3feeca 100644 --- a/.cursor/rules/f2s-flow2spec-unified-entry.mdc +++ b/.cursor/rules/f2s-flow2spec-unified-entry.mdc @@ -29,7 +29,7 @@ alwaysApply: true ## 读取顺序(必须) -1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;按需根据 `matcherPath` 读取 matcher 分片获取 `includeAny` 关键词;无法命中时进入补召回阶段。 +1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;初筛证据为每条规则的 `task` 名与 `summary`(一句话意图摘要);按需根据 `matcherPath` 读取 matcher 分片获取词表(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词;否决门恒胜,优先于 `task` 精确命中,详见 `topics/kb-routing-summary.md`);无法命中时进入补召回阶段。 - 若命中主题在 `topicDependencies` 中存在依赖,先读依赖主题,再读主主题。 - 路由清单仅通过 `f2s-*` 技能流程维护,不依赖额外 CLI 子命令。 2. `.Knowledge/index.md` 按需读取,仅用于确认主题语义与边界。 @@ -37,9 +37,9 @@ alwaysApply: true 4. 若需要背景,再读 `.Knowledge/stock-docs/.md`。 5. 仅在前四步不足时下钻业务源码。 6. 命中后必须执行 `match -> expand -> verify -> act`: - - `match`:先取主候选; + - `match`:以规则的 `task` 名与 `summary` 做语义匹配取主候选;`summary` 与 `includeAny` 均为语义锚而非字面白名单,允许近义命中; - `expand`:展开 `topicDependencies`,并保留次高候选做补充校验; - - `verify`:执行前做缺口检查(关键主题/边界/上下文是否缺失); + - `verify`:执行前做缺口检查(关键主题/边界/上下文是否缺失);**若命中主题正文未覆盖用户问句的核心名词,必须并读次高候选的 `summary` 与 matcher 分片再定,不得直接作答**; - `act`:仅在置信度足够时执行;低置信度必须先澄清。 7. 仅在以下条件之一成立时,允许执行跨 matcher 全量补检索(top-k): - `taskToTopicRules` 无命中; @@ -55,11 +55,12 @@ alwaysApply: true ## 机读事实源口径(规则层) - `taskToTopicRules`:任务路由第一优先级。 +- `taskToTopicRules[].summary`:初筛召回字段(一句话意图摘要),由 `flow2spec kb build` 从 topic frontmatter `summary` 机械同步,不手写 manifest 侧;作语义锚参与 match 初筛。 - `taskToTopicRules[].matcherPath`:匹配词分片直链路径,按需读取单个 matcher 文件。 - `taskToTopicRules[].matcherId`:matcher 的稳定标识,需与 matcher 分片内 `id` 一致。 - `topicDependencies`:主主题命中后先加载依赖主题。 - `topicMetadata`:主题治理元数据,只影响阅读预期,不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性;执行强制性始终以 `AGENTS.md`、rules、skills 与 topic 正文中的明确要求为准。读到 `topicMetadata[topicId].primary` / `tags` 时:`config` 关注配置项、开关、默认值、初始化参数;`policy` 优先检查正文中的必须/禁止/门禁/流程约束;`feature` 作为已落地业务/产品能力背景;`module` 作为目录、包、模块边界与工程结构背景。`confidence` 仅允许 `manual` / `inferred`;无明确分类证据时不写 metadata。 -- `matcherPath(includeAny)`:任务关键词匹配词表。 +- `matcherPath(includeAny/includeAll/excludeAny/excludeAll)`:任务关键词匹配词表(前两者为资格门,后两者为否决门;详见 `topics/kb-routing-summary.md`)。 - `fallbackTopic`:任务与关键词都未命中时必须读取,但仅作低置信度兜底,不是最终执行依据。 - `.Knowledge/manifest-routing.json + matcherPath 分片文件` 是机读事实源(关键词仅在 `matchers/*.json`)。 - `.Knowledge/index.md` 不是机读事实源,仅作人读导航与语义边界校验。 @@ -69,7 +70,7 @@ alwaysApply: true | 情况 | 对策 | | --- | --- | -| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` 覆盖用户常用说法。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | +| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` / `includeAll` 覆盖用户常用说法,必要时用 `excludeAny` 排除误路由。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | | **1b 命中了但上下文不够** | 先 `expand`(`topicDependencies` + 次高候选),再 `verify` 点名缺哪份 `stock-docs`/`req-docs` 或哪段 topic;仍不足则 **向用户要文档或路径**,不要无门槛跨 matcher 全量补检索。**Agent 若需下钻源码**:须先对用户做**可见的缺口说明**(已读 KB、缺什么、拟读哪 1~2 个文件),见 **`f2s-knowledge-preflight`**「缺口闸门」;**禁止**无说明地连续 `Grep`/乱序探源。 | | **2 库里没有对应文档** | 一次读完 routing + 已命中 matcher + 相关 topic 后,在回复中 **明确承认知识库无覆盖**,再选:下钻业务代码 / 请用户补充 `req-docs` 或 PRD。**禁止**用反复读清单假装「再找一遍就会有」。**下钻源码前**同样须满足 **`f2s-knowledge-preflight`**「缺口闸门」的可见说明。 | | **2a 反复读清单耗 token** | **同一任务线内** `manifest-routing.json` 视为稳定快照:再次全文读取须说明理由(例如用户声明已通过 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 等更新路由或知识、或**手动编辑**了 manifest/matcher)。**勿将**仅执行 **`flow2spec init`** 等同于「业务知识库已更新」:`init` 以配置根落盘、目录补齐与包级路由结构对齐为主;**stock-docs / req-docs、topics 路由摘要、matchers 词条**由 **`f2s-*` 技能流程**维护;`init` 会把规则写入配置根 **`rules/*`**(或等价扩展名),并为 Codex 写入 **`.codex/topics/*.md`**。只读 **当前规则对应的单个** `matcherPath`;不要为枚举而遍历整个 `matchers/` 目录。`index.md` 仅在需核对主题语义时打开,禁止与 manifest 交替「刷清单」。 | @@ -97,9 +98,9 @@ alwaysApply: true **规则层双保险**(与脚本缓存互为备份): 1. 读 `flow2spec.config.json` → 若 `updateCheck.enabled` 不为 `true`,跳过,不做任何提示。 -2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日(`new Date(checkedAt).toDateString() === new Date().toDateString()`),不重复查 npm;但若 `needsUpgrade=true` 或 `latestNpm > manifestVersion`,本会话首次回复用户时仍须转告升级提示,并可**直接替用户执行** `flow2spec init <已初始化的 agents>` 完成更新——init 后读 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` 相等(非主题版本更新)即完成并删除该缓存;不等(含主题层变更)再执行 `f2s-kb-upgrade` 完整流程(可从其步骤 2c 起,不重复 init);若当前 `.Knowledge/manifest-routing.json.version` 已不低于 `latestNpm`,删除该缓存并不再提示。 +2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日,不重复查 npm;`coreUpdateAvailable=true` 时可执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新)。若 `templateUpdateAvailable=false`,随后只执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook,删除缓存,不进入 `f2s-kb-upgrade`;若 `templateUpdateAvailable=true`,更新后执行 init,再以 `projectRev` / `pkgRev` 判定快速路径或完整流程。`.Knowledge/manifest-routing.json.version` 表示 Template Version,禁止与 Core Version 直接比较。 3. 上述两步均未跳过时:执行当前 agent 配置根下的更新检测脚本(Claude:`node .claude/hooks/f2s-update-check.js`;Cursor:`node .cursor/hooks/f2s-update-check.js`;Codex:`node .codex/hooks/f2s-update-check.js`),解析标准输出的 JSON: - - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 执行(非主题版本更新可直接代跑 `flow2spec init`;主题层变更再执行 `f2s-kb-upgrade` skill)。 + - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 分别处理 Core-only 与 Template 更新。 - 无输出或解析失败:静默,不提示。 4. 以上步骤出现任何错误,静默跳过,不影响正常对话。 diff --git a/.cursor/rules/f2s-kb-feedback-closing.mdc b/.cursor/rules/f2s-kb-feedback-closing.mdc index 857f904..c9f815c 100644 --- a/.cursor/rules/f2s-kb-feedback-closing.mdc +++ b/.cursor/rules/f2s-kb-feedback-closing.mdc @@ -107,6 +107,12 @@ description: 普通问答读取源码后的知识库补充建议收口规则; 这样能避免"已有 topic 还建议 add"的误判。 +## 路由 miss 反哺(强制) + +- **判定**:本轮答案的核心事实由某 topic 提供,但该 topic **不是初筛主命中**(靠全量补检索、次高候选并读或用户点名才找到)→ 计为一次路由 miss。 +- **动作**:在四 case 收口之外,把本轮用户的原始问法回填该 topic 的召回锚——按 `f2s-topic-authoring`「初筛召回规范」补 frontmatter `summary` 的核心名词或 matcher `includeAny` 的单概念词,并跑 `flow2spec kb build` 同步 rule.summary。此项是直接落盘的最小修复,不依赖用户再跑 distill。 +- 即使收口走 case 4(知识库已覆盖),只要命中路径是 miss 后补救,仍须执行本反哺,并在收口块后追加一行:`已回填路由: 的 summary/includeAny 补「<词>」`。 + ## 输出格式 - case 1~3:输出一个 Markdown 引用块,依次写 `f2s-kb-distill` 命令 + 一行空行 + **本轮将入库**概要(一句话,见上文「概要要求」)。 diff --git a/.cursor/rules/f2s-topic-authoring.mdc b/.cursor/rules/f2s-topic-authoring.mdc index e454ec9..2cae945 100644 --- a/.cursor/rules/f2s-topic-authoring.mdc +++ b/.cursor/rules/f2s-topic-authoring.mdc @@ -32,13 +32,49 @@ alwaysApply: false 每个 topic 至少包含: 1. **标题与一句话意图**(一行写清"该 topic 解决什么"); -2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` 语义一致); +2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` / `includeAll` 语义一致); 3. **核心规则 / 流程**(可执行知识;步骤须可由 Agent 复现); 4. **依赖声明**(若 `topicDependencies` 中存在依赖项,正文须显式写一句「执行前须先读依赖主题 ``」,参考 `topics/f2s-req-plan.md` 首段写法); 5. **边界与禁止项**(避免膨胀到隔壁 topic); 6. **长文背景 / 详细资料引用**(如需承载业务背景):只列 `.Knowledge/stock-docs/*_终稿.md` 的可点击 Markdown 链接(1–3 条即可);**禁止**直接列 `.Knowledge/req-docs/*` 作为长文背景来源;无对应终稿时**先生成终稿**再回填此小节。 -## 3. topicMetadata 判定准则 +### frontmatter `summary` 与 `includeAny` 创作规范(初筛召回,硬约束) + +topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest-routing.taskToTopicRules[].summary`,是 Agent 初筛的常驻语义锚——写作质量直接决定该 topic 能否被自然问法命中: + +- **`summary`**:一句话写清「职责 + 用户会问的核心名词」,覆盖 topic 的全部职责域(如同时承担“职责边界”与“资料入口”,两者都要出现);软上限 30 字(英文 15 词),硬上限 40 字 / 20 词(`kb check` 校验);禁止 `(路由摘要)`、`TODO` 等占位写法。 +- **`includeAny`**:单概念核心词优先(自然问法中会独立出现的最小词,如「原型」「流程图」而非「原型位置」「业务流程图」);复合词仅作补充;从用户口述沉淀时须补 1–2 个“日后自然问法”词,不只抄口述原文。 +- **自测(落盘前必做)**:模拟 2–3 个用户自然问句(凭空想“用户会怎么问”,不看词表),检查每句至少命中 `summary` 或 `includeAny` 之一;不命中则回改。 +- **`taskToTopicRules[].task`**:保持稳定 id 语义(kb 引擎按 `task` 合并),不塞长短语;召回语义由 `summary` 承载,manifest 侧由 `kb build` 生成,不手写。 + +## 3. matcher 分片字段语义 + +matcher 分片(`.Knowledge/matchers/.json`)有 4 个字段参与 `routing.match()` 判定,按「否决门 → 资格门」两级: + +- **资格门**(至少满足其一即候选): + - `includeAny`:任一短语命中(OR)——最常用 + - `includeAll`:所有短语全部命中(AND)——表达组合词/上下文约束 +- **否决门**(任一门命中即整条规则出局,**恒胜,优先于 `task` 精确命中**): + - `excludeAny`:任一短语命中(OR) + - `excludeAll`:所有短语全部命中(AND);半命中不否决 + +写作准则: + +1. **默认只写 `includeAny`**:大部分主题不需要否决词/AND 词;泛化的单概念核心词交给 `includeAny` 即可。 +2. **用 `excludeAny` 排除误路由**:当同一批 `includeAny` 词在**另一场景**下反复被错命中(如 "部署" 词被 "预演部署" 误命中),用 `excludeAny: ["预演", "preview"]` 精确排除,**保留召回率**——比删触发词代价小得多。 +3. **用 `includeAll` 表达组合词**:仅当**两个词单独都太泛、但同时出现才有意义**时使用(如 "数据 + 迁移");否则拆成两条 `includeAny` 更好。 +4. **用 `excludeAll` 表达"仅当同时否定"**:仅当"任一词单独出现都不应否决,但两个词同时出现明确不属于本任务域"时使用(如 "归档 + 下线");这是罕见场景,大多数否决需求用 `excludeAny` 就够。 +5. **否决优先于 `task` 精确命中**:即使调用方显式传入 `task: "deploy"`,只要 `excludeAny` 命中,该规则仍然出局——排除词表达的是「此请求不属于该任务域」,语义强于精确命中。**若你不想让 `task` 参数被否决,不要给这条 rule 写 `excludeAny`**。 +6. **打分池合并**:`includeAll` 命中的短语与 `includeAny` 命中一起进入打分池,`score` 取最高短语分,不改变 `confidence` 分档规则(exact task=high、≥30=medium、<30=low)。 + +阈值提示: + +- `includeAny` 词表超过 12 个通常是主题过宽信号(见第 6 节拆分策略);若通过 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估。 +- 排除词无阈值约束,但写超过 5 条通常意味着资格词已过泛,应回头收紧资格门。 + +实现:`packages/core/lib/routing.js:match()`;契约测试:`scripts/test-routing-semantics.js`(10 组用例覆盖 4 字段判定门与 exact-task 交互)。 + +## 4. topicMetadata 判定准则 `topicMetadata` 是治理元数据,只影响盘点、审计和阅读预期;不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性。执行强制性以 `AGENTS.md`、rules、skills 与 topic 正文明确要求为准。 @@ -61,7 +97,7 @@ alwaysApply: false 禁止:为了分类创建、重命名、拆分 topic;在 topic markdown 正文或 `index.md` 中重复写分类块。 -## 4. topicDependencies 判定准则 +## 5. topicDependencies 判定准则 设当前主题为 A、候选依赖为 B。**四问命中任一即声明 `A → B`**: @@ -81,14 +117,14 @@ alwaysApply: false **判定时机**:终稿与新 / 改 topic 落盘后,扫正文中**反引号引用的其他 topic id 与规则文件名**,逐个套四问;命中即写入 `manifest-routing.topicDependencies`,**并在新 topic 正文显式写依赖声明**(见骨架第 4 条)。 -## 5. 大功能拆分策略 +## 6. 大功能拆分策略 当一个业务功能体量较大时,推荐「主 topic + 子 topic」结构,而非单个大 topic。 **何时拆分(软约束,满足任一评估是否需拆)**: - 对应 stock-doc 超过 **300–500 行**:建议评估拆分,不强制阻断; -- matcher `includeAny` 超过 **12 个**:主题过宽信号; +- matcher `includeAny` 超过 **12 个**:主题过宽信号(若用 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估,详见第 3 节); - topic 正文包含超过 **3 个不相干职责域**的二级标题; - `f2s-kb-upgrade` 审计时发现同一 topic 被多种不相干任务类型反复命中。 @@ -100,10 +136,10 @@ alwaysApply: false **不要做的事**: -- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 4 节反向排除); +- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 5 节反向排除); - 不为拆分而强行制造子 topic,若子模块本身不会被独立路由命中,不必建 topic。 -## 6. rule 是否需新建对应 topic +## 7. rule 是否需新建对应 topic 判据:**该 rule 是否会作为用户任务路由命中**。 @@ -112,7 +148,7 @@ alwaysApply: false 误区:「重要的规则就该有 topic」——重要不等于"用户路由命中";让消费方 SKILL 在正文里直接 `Read rules/.*` 全文即可,无需走 manifest 路由。 -## 7. 写盘权属(指针) +## 8. 写盘权属(指针) `manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/topics/*.md` 的写权约束**以 `f2s-flow2spec-unified-entry` 与各 SKILL 内「写权硬约束」为准**,本条不复述;遇分歧以统一入口与对应 SKILL 为准。 @@ -121,7 +157,7 @@ alwaysApply: false - 在未读本条的情况下新增 / 修改 topic 或 `topicDependencies`。 - 为补分类单独创建、重命名或拆分 topic。 - 在 topic 正文或 `index.md` 中写 `## 概念分类` 等 metadata 副本。 -- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 4 条)。 +- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 7 条)。 - 用 `topicDependencies` 表达"信息相关"(应通过 `index.md` 语义边界 + matcher 关键词补召回,而非依赖边)。 - 在 `topicDependencies` 中写传递冗余边或形成环。 - **在 topic 的「长文背景 / 详细资料 / 相关资料 / 长文来源 / 参考文档」等指向长文源的整节引用槽位里,列出 `.Knowledge/req-docs/*`**(含澄清 / 技术方案 / SQL / PRD)。此类槽位只允许指向 `.Knowledge/stock-docs/*_终稿.md`;无终稿时须先生成再回填。短句/佐证式的偶发点引不受此约束。 \ No newline at end of file diff --git a/.cursor/skills/f2s-kb-addRules/SKILL.md b/.cursor/skills/f2s-kb-addRules/SKILL.md index 93bc55f..f62b94d 100644 --- a/.cursor/skills/f2s-kb-addRules/SKILL.md +++ b/.cursor/skills/f2s-kb-addRules/SKILL.md @@ -83,7 +83,7 @@ description: 把用户口述的规则沉淀进知识库,自动判定「新建 - 补 `manifest-routing.topicPaths`:` -> .Knowledge/topics/.md`; - 按需补 `manifest-routing.topicMetadata`:口述规则主题通常为 `{ "primary": "policy", "confidence": "inferred" }`;用户明确确认分类可写 `manual`;如同时包含配置项 / 模块 / 能力性质,可写入不与 `primary` 重复的 `tags`;证据不足则不写 metadata,并在摘要列为待确认。分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性; - 视情况补 `taskToTopicRules[]`——**仅当**该规则会作为**用户任务路由命中**(参见 `f2s-topic-authoring` 第 5 节判据)才补;纯被其它规则 / SKILL 引用的内部规则**不进** `taskToTopicRules`; - - 若补了 `taskToTopicRules[]`,须新建 `.Knowledge/matchers/.json`,从用户口述中抽取 `includeAny` 关键词(用户原话 + 1–2 个明显近义说法,宁缺勿滥); + - 若补了 `taskToTopicRules[]`,须新建 `.Knowledge/matchers/.json`,`includeAny` 按 `f2s-topic-authoring`「初筛召回规范」抽取:**单概念核心词优先**,复合词仅作补充;除用户口述原词外须补 1–2 个“日后自然问法”词,并用 2–3 个模拟问句自测命中;topic frontmatter `summary` 同步按该规范写(kb build 会将其同步进 rule.summary 作初筛锚); - **并入存量主题**: - `topicPaths` 不变; - 可按需补齐该 topic 的 `topicMetadata`,但不得为了分类创建、重命名或拆分 topic; diff --git a/.cursor/skills/f2s-kb-build/SKILL.md b/.cursor/skills/f2s-kb-build/SKILL.md index bda9c7b..15b5ec2 100644 --- a/.cursor/skills/f2s-kb-build/SKILL.md +++ b/.cursor/skills/f2s-kb-build/SKILL.md @@ -47,9 +47,10 @@ description: 根据 .Knowledge/stock-docs 文档生成知识路由主题与索 从文档中提炼: - 主题名与主题意图(可形成 topic id) +- frontmatter `summary`(初筛召回锚:职责 + 用户会问的核心名词,按 `f2s-topic-authoring`「初筛召回规范」写;kb build 会同步进 manifest rule.summary) - 核心概念与关键流程 - 业务规则与边界条件 -- 任务触发词(写入对应 `matchers/.json` 的 `includeAny`) +- 任务触发词(写入对应 `matchers/.json` 的 `includeAny`;单概念核心词优先,复合词仅作补充) - 与现有主题的依赖关系(用于 `topicDependencies`) > **创作侧准则**:本步骤涉及新增 / 修改 topic 与 `topicDependencies`,**须先 Read** `rules/f2s-topic-authoring.*` 全文(**Cursor/Claude**:`rules/f2s-topic-authoring.mdc`;**Codex**:`.codex/topics/f2s-topic-authoring.md`),再继续步骤 3 / 步骤 5。命名、骨架、依赖判定、DAG 最小化、判定时机均以该条为准,本 SKILL 不复述。 diff --git a/.cursor/skills/f2s-kb-distill/SKILL.md b/.cursor/skills/f2s-kb-distill/SKILL.md index c2f923b..496ebf5 100644 --- a/.cursor/skills/f2s-kb-distill/SKILL.md +++ b/.cursor/skills/f2s-kb-distill/SKILL.md @@ -285,12 +285,14 @@ description: 从问答过程中提取可复用知识事实并自动入库;根 - 创建对应的 `matchers/.json`,包含: - 从用户问题中提取的关键词 - 从回答中提取的术语 - - 建议 `includeAny`:5-10 个触发词 + - 建议 `includeAny`:5-10 个触发词(单概念核心词优先,复合词仅作补充;见 `f2s-topic-authoring`「初筛召回规范」) - 在 `taskToTopicRules` 中添加路由规则 + - topic frontmatter `summary` 按「初筛召回规范」写(职责 + 用户会问的核心名词;kb build 会同步进 rule.summary) - 如果更新既有 topic: - 检查 matcher 是否需要补充新的触发词 - 从用户问题中提取未覆盖的关键词,追加到 `includeAny` + - 若本轮问法未被该 topic 的 `summary` 覆盖,回改 frontmatter `summary` 补上核心名词 #### 5.2 更新 index.md @@ -373,7 +375,7 @@ description: 从问答过程中提取可复用知识事实并自动入库;根 - 不需要用户确认(问答已验证知识的正确性) - 保持轻量,单次问答的知识提取在 30 秒内完成 - 避免过度拆分:除非下钻深度 ≥ 深且知识描述深度 ≥ 详细级,否则优先补充既有 topic -- 生成的 matcher includeAny 应覆盖用户实际会用的表述,不只是技术术语 +- 生成的 matcher includeAny 应覆盖用户实际会用的表述,不只是技术术语;topic frontmatter `summary` 同步覆盖本轮问法的核心名词 ## 完成后自检 diff --git a/.cursor/skills/f2s-kb-sync/SKILL.md b/.cursor/skills/f2s-kb-sync/SKILL.md index 6d69ea2..922569a 100644 --- a/.cursor/skills/f2s-kb-sync/SKILL.md +++ b/.cursor/skills/f2s-kb-sync/SKILL.md @@ -87,7 +87,7 @@ description: 可显式给出能力或零输入推断;先输出知识库更新 - `.Knowledge/topics/*.md` - `.Knowledge/index.md`(同步主题路由表的“关联文档(摘要)”列) -- 路由清单(按需);若创建新 topic,须同步 `topicPaths`、必要的 `taskToTopicRules` / matcher 分片;可在证据明确时写 `topicMetadata`,但分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性,不得为了分类创建、重命名或拆分 topic +- 路由清单(按需);若创建新 topic,须同步 `topicPaths`、必要的 `taskToTopicRules` / matcher 分片;新建 / 更新 topic 的 frontmatter `summary` 与 matcher `includeAny` 按 `f2s-topic-authoring`「初筛召回规范」写(summary 由 kb build 同步进 rule.summary);可在证据明确时写 `topicMetadata`,但分类只用于治理、审计和阅读预期,不参与路由命中或执行强制性,不得为了分类创建、重命名或拆分 topic - `.Knowledge/stock-docs/*.md`(按需补充索源文档) ### 步骤 4:收尾摘要 diff --git a/.cursor/skills/f2s-kb-upgrade/SKILL.md b/.cursor/skills/f2s-kb-upgrade/SKILL.md index 8b94ed1..1871347 100644 --- a/.cursor/skills/f2s-kb-upgrade/SKILL.md +++ b/.cursor/skills/f2s-kb-upgrade/SKILL.md @@ -12,12 +12,12 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 边界(避免误区) - **`flow2spec init` 不写业务知识**:不替代 `f2s-kb-add`、`f2s-kb-fix`、`f2s-kb-feat`、`f2s-kb-sync`、`f2s-kb-build` 等对 `stock-docs` / `req-docs` / `topics` 正文与业务向路由词条的维护。 -- 本技能跑通的是 **包版本下的目录、模板占位、路由结构对齐**;用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 +- 本技能跑通的是 **Template Version 下的目录、模板占位、路由结构对齐**;Core 代码版本与 Template Version 独立。用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 - 本技能负责存量 `topicMetadata` 审计:`primary` / `tags` 仅用于治理、审计、盘点和阅读预期,不参与路由命中或执行强制性;执行强制性仍以 `AGENTS.md`、rules、skills 与 topic 正文为准。 ## 包侧发版纪律(`projectRev` 必须正确 bump) -**字段位置**:`templates/{zh-CN,en-US}/knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 +**字段位置**:Core 包模板 `knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 **字段写入语义(必读)**: - **包侧**:维护者按下文规则手动 bump(包模板自身的 `projectRev` 永远是最新值)。 @@ -27,15 +27,15 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( - 这使「项目侧 `projectRev`」语义清晰:**「本项目已基线对齐到的包模板修订号」**,而非"上次 init 时碰到的"。 **必须 bump 的修改**(每次发版至少 `+1`): -- 包模板 `templates//knowledge/topics/.md` 任一文件的**正文**修改、新增、删除或改名; -- 包模板 `templates//knowledge/matchers/.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; -- 包模板 `templates//knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; -- 包模板 `templates//knowledge/index.md` 「主题一览」节或包级章节修改。 +- 包模板 `knowledge/topics/.md` 任一文件的**正文**修改、新增、删除或改名; +- 包模板 `knowledge/matchers/.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; +- 包模板 `knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; +- 包模板 `knowledge/index.md` 「主题一览」节或包级章节修改。 **不需要 bump 的修改**: - 包源码(`lib/`、`cli.js`、`scripts/`)、`AGENTS.md`、`README*` 文档; -- `templates//flow2spec.config.json` 默认值; -- `templates//rules/*` / `templates//skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程)。 +- `flow2spec.config.json` 默认值; +- `rules/*` / `skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程,但仍须显式提升 Template Version 以便 Hook 分发新版内容)。 **判定准则一句话**:模板里 `knowledge/` 目录下 topic / matcher / manifest / index 任一**主题层产物**变了 → 必 bump;否则不动。漏 bump 会让用户的 `f2s-kb-upgrade` 跑快速路径,错过包带来的主题变更。 @@ -54,11 +54,12 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 非主题版本更新:agent 可直接代跑 `init`(无需进入本技能) -版本检查提示「知识库版本低于最新包版本」时,agent 可**直接替用户执行** `flow2spec init <已初始化的 agents>` 完成更新: +版本检查提示 Core 更新但 `templateUpdateAvailable=false` 时,agent 可**直接替用户更新 Core 并执行** `flow2spec init <已初始化的 agents>`: - `init` 后读项目侧 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` **相等**(非主题版本更新)→ 更新完成,删除 `.Knowledge/update-check.json`,**无需进入本技能**; - 两者**不等**(包含主题层变更)→ 进入本技能完整流程(从步骤 2c 起判定,不重复 `init`)。 - 该路径与本技能步骤 2c「快速路径」同一判定口径;用户显式要求「完整流程 / 覆盖重置」时仍按本技能全文执行。 +- `.Knowledge/manifest-routing.json.version` 表示 Template Version;禁止拿它与 Core Version 直接比较。 ## 旧版布局(V1)不再内置迁移 @@ -97,39 +98,30 @@ description: 知识库模板升级技能(仅指本 SKILL):**现行库( ## 强制流程 -### 步骤 -1:全局 flow2spec 版本预检(必须,先于一切,主 agent 前台探测) +### 步骤 -1:CLI/Core/Template 版本预检(必须,先于一切) -**目的**:让「能用全局 `flow2spec` 就用全局」,只在**没装**或**版本过旧**时才动手升级;已装且已是 latest 时**完全跳过**升级动作,同时决定步骤 2 命令的**默认形态**(用 `flow2spec init` 还是 `npx @latest init`)。 - -**动作**:主 agent 在进入步骤 0 **之前**,**顺序、前台**执行以下 3 条探测(都是纯查询,无副作用,秒级返回;无需拆子 agent): +主 agent 前台执行只读探测: ```bash -# 1. 探测本机全局是否装了 flow2spec -flow2spec --version 2>/dev/null || echo __F2S_NOT_INSTALLED__ -# 2. 查询 npm 上 latest 版本号(网络受限时可能失败,允许失败) -npm view @double-coding/flow2spec version 2>/dev/null || echo __F2S_NPM_UNREACHABLE__ -# 3. (备用)若第 1 步返回 __F2S_NOT_INSTALLED__,用来确认 npx 可用 -command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ +flow2spec version +flow2spec update --check ``` -**判定 3 分支**(按结果选一条,写入本轮上下文并影响步骤 2 与步骤 5 摘要): - -| 情况 | 判定条件 | 行动 | 步骤 2 命令默认形态 | -| --- | --- | --- | --- | -| **A. 已装且是 latest** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V === L` | **完全跳过升级**,本轮不派子 agent、不跑 `npm i -g` | **`flow2spec init `**(用全局) | -| **B. 已装但落后** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V !== L`(`V < L` 或 semver 不等) | **派独立子 agent 后台跑** `npm i -g @double-coding/flow2spec@latest`(fire-and-forget,不等待,不阻塞主流程);本轮步骤 2 仍用 `npx @latest` 保证本次拿到 latest 模板 | **`npx @double-coding/flow2spec@latest init `** | -| **C. 未装 or 版本无法确认** | 第 1 步命中 `__F2S_NOT_INSTALLED__`,或第 2 步命中 `__F2S_NPM_UNREACHABLE__` 且第 1 步也未拿到版本号 | 若 A 情况「已装 latest」不成立且**未装**:派独立子 agent 后台跑 `npm i -g ...@latest`(同 B);若第 2 步失败但第 1 步已装某版本:视作 B 且无法比对 latest,**不派**升级、仅提示「latest 未知,保守用 npx」 | **`npx @double-coding/flow2spec@latest init `** | +按输出记录 CLI Version、Core Version、Core Pinned、Template Version、Protocol Version,以及 npm 最新 Core/Template(CLI 对 Core 为精确 pin,两包联动发布): -**编排(必须)**: +| 情况 | 行动 | 步骤 2 默认命令 | +| --- | --- | --- | +| **A. Template 已是最新** | 若 CLI/Core 有更新,执行 `flow2spec update --cli`(CLI 与配套 Core 联动更新)后幂等 init 刷新 Hook;随后**不得直接停止**,必须先做下方「**项目侧对齐检查**」,通过后才删除缓存并停止本技能 | `flow2spec init ` | +| **B. Template 有更新** | 执行 `flow2spec update --cli`(CLI 与配套 Core 一起到位),继续步骤 0 | `flow2spec init ` | +| **C. 未安装或版本未知** | 使用 latest CLI(自带 pin 的配套 Core),避免 npx 复用旧版缓存 | `npx --yes @latest init ` | -- **A 分支**:主 agent 直接跳过所有升级动作,**不派**子 agent;本轮步骤 2 命令首选 `flow2spec init`。 -- **B / C 分支**:若确需升级(未装或版本落后),派**独立子 agent** fire-and-forget 执行 `npm i -g @double-coding/flow2spec@latest`,**不等待完成**、**不阻塞**主流程;成败均不进入 SKILL 结论。该派子**强制**执行,**不受** `flow2spec.config.json.subAgent` 字段约束(全局 npm 装包不属业务拆分范畴)。 -- **写权**:子 agent 仅执行该 shell,**不**触碰 `.Knowledge` / `manifest-routing.json` / `index.md` 等任何项目文件;写权硬约束不变。 -- **探测失败兜底**:若 3 条探测全部失败(无 shell 权限、极端受限环境),按 C 分支处理并用 `npx @latest`;此时也可以直接放弃步骤 -1、把升级留给 `cli.js` 的 `maybeAutoUpdateGlobalInstall()` 收尾兜底。 +预检失败时允许回退 C,但不得把 Core Version 当作 Template Version。此步骤不强制创建子 agent,也不后台安装全局包。 -**与 cli.js 的关系**: +**项目侧对齐检查(A 分支停止前必须)**:`flow2spec version` / `update --check` 对比的是**本机包 vs npm 最新**,不反映**本项目 `.Knowledge` 是否已对齐当前包模板**(典型误判:刚升级完 CLI/Core 后首次在老项目跑本技能,包侧「均为最新」但项目侧模板仍是旧版,直接停止会跳过全部升级动作)。停止前逐项确认: -- `cli.js` 内 `maybeAutoUpdateGlobalInstall()` 是 `init` 收尾兜底逻辑,**与本步不冲突**:本步在前台 init 之前完成探测/派工,cli 那段在 init 收尾时再兜一次;两次都成功就是 no-op,第一次失败第二次还能补救。 +1. Read 项目侧 `.Knowledge/manifest-routing.json`:若 `version`(项目侧 Template Version)**低于**本机包 Template Version,或 `pkgRev` / `projectRev` 字段缺失 → 项目侧未对齐,**视同 Template 更新**:继续步骤 0 → 2(init)→ 2c 判定,不得停止; +2. 字段一致时再执行 `flow2spec kb check --strict`:若报 summary 质量 warning 或结构问题 → 转步骤 3a.7 / 3a.8 处理完再收尾; +3. 以上均通过才允许按 A 分支停止,并在摘要中写明「项目侧已对齐(version=X,strict 通过)」。 ### 步骤 0:版本判定与分流(必须,先于 init) @@ -165,10 +157,10 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 在目标项目根目录执行以下命令(**按步骤 -1 的分支结论选默认形态**): -1. **步骤 -1 判定为 A(已装且是 latest)**:直接用全局 CLI(**首选**): +1. **步骤 -1 判定为 A/B(本地 CLI/Core 可用)**:直接使用当前 CLI: - `flow2spec init ` -2. **步骤 -1 判定为 B/C(未装 / 落后 / latest 未知)**:拉 npm latest 跑(**保证本次拿到最新模板**): - - `npx @double-coding/flow2spec@latest init ` +2. **步骤 -1 判定为 C**:用 latest CLI(自带 pin 的配套 Core 与模板): + - `npx --yes @latest init ` 3. 覆盖重置时: - 在上述命令末尾追加 `--reset-knowledge` 4. 用户显式要求切换模板语言时: @@ -177,7 +169,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ > `` 示例:`cursor claude codex`。 -> **辅助命令(用户可自查)**:`flow2spec --version` 看当前全局版本;`flow2spec update` 触发 CLI 内置的自更新。这两条**不**替代本 SKILL 的完整流程——它们只是「让全局 CLI 保鲜」,主题层对齐仍须走本 SKILL 步骤 2 及以后。 +> **辅助命令(用户可自查)**:`flow2spec version` 查看五维版本;`flow2spec update --check` 检查更新,`flow2spec update --cli` 整体更新(CLI 与配套 Core 联动,`--core` 为其等价别名)。这些命令不替代 Template Version 变化后的本技能完整流程。 **步骤 2 完成后**:立刻执行上文 **「init 与技能自更新」**:重读 **`skills/f2s-kb-upgrade/SKILL.md`**;若有更新则**按新版字面从步骤 2c 起重跑**(**不再次 init**;避免用旧版 SKILL 做后续校验)。 @@ -243,7 +235,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ - `includeAny` 词数超过 **12 个**; - topic 正文包含超过 **3 个不相干职责域**的二级标题; - 该 topic 同时被多种不相干任务类型频繁命中(可从 `taskToTopicRules` 和 matcher 词宽度判断)。 -7. **旧 topic frontmatter 自动补齐**:完整流程中必须由 agent 自行执行 `flow2spec kb build --fix-topics`(或等价内部能力),为缺少 frontmatter / `revision` 的存量 topic 补 `id`、`revision`、`summary`,并按 `manifest-routing.json` 补 `dependsOn` / `primary` / `confidence` / `tags`。随后执行 `flow2spec kb check --strict`;若 strict 失败,停止并在摘要中列出具体 topic / reason。不得要求用户手动逐个 topic 添加头部。 +7. **旧 topic frontmatter 自动补齐**:完整流程中必须由 agent 自行执行 `flow2spec kb build --fix-topics`(或等价内部能力),为缺少 frontmatter / `revision` 的存量 topic 补 `id`、`revision`、`summary`,并按 `manifest-routing.json` 补 `dependsOn` / `primary` / `confidence` / `tags`。随后执行 `flow2spec kb check --strict`;若失败项为 **summary 质量 warning**(缺失 / 占位 / 超长),转第 8 条补写;其余失败停止并在摘要中列出具体 topic / reason。不得要求用户手动逐个 topic 添加头部。 +8. **summary 初筛锚补写(必须,agent 才能写出语义摘要)**:对 `kb check --strict` 报出的每个 summary 质量 warning,逐个 Read 对应 `.Knowledge/topics/.md` 正文,按 `f2s-topic-authoring`「初筛召回规范」补写 frontmatter `summary`(职责 + 用户会问的核心名词,软 30 字 / 硬 40 字);全部补完后执行 `flow2spec kb build` 同步进 `rule.summary`,再跑 `flow2spec kb check --strict` 直至无 summary warning。禁止用占位文案(`(路由摘要)` / TODO)敷衍通过。 ### 步骤 3b:`index.md` 融合与 `template/index.template.md`(必须执行) @@ -300,7 +293,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 输出以下信息: -- **步骤 -1 全局版本预检**:分支结论(`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级) / C 未装或 latest 未知(已派或提示)`)+ 当前全局版本 + npm latest 版本(若拿到) +- **步骤 -1 全局版本预检**:分支结论(`A 已装且是 latest(需附项目侧对齐结论) / B 已装但落后(已派子 agent 后台升级) / C 未装或 latest 未知(已派或提示)`)+ 当前全局版本 + npm latest 版本(若拿到) +- **项目侧对齐检查**(A 分支时必填):`已对齐(version=X,strict 通过,已停止)` / `未对齐(version X < 包 Y,已转完整流程)` / `strict 报 warning(已转 3a.7/3a.8)` - 执行命令(含 agent 与是否 reset) - 是否成功 - **`projectRev` 判定**:`projectRev` X vs `pkgRev` Y → 快速路径 / 完整流程 / 字段缺失走兜底(步骤 2c) @@ -319,8 +313,8 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ ```markdown ## f2s-kb-upgrade 执行结果 -- **步骤 -1 全局版本预检**:`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=``,latest=`` -- 本技能内代跑命令:`<实际执行的 flow2spec init ... 或 npx @latest init ...>` +- **步骤 -1 全局版本预检**:`A 已装且是 latest(项目侧对齐:<结论>) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=``,latest=`` +- 本技能内代跑命令:`<实际执行的 flow2spec init ... 或显式 latest CLI/Core 组合 init ...>` - init 模式:`增量` / `覆盖重置(--reset-knowledge)` - 执行结果:`成功` / `失败` - **主题层判定**:`projectRev=` vs `pkgRev=` → `快速路径(已跳过 3/3a/3b)` / `完整流程` / `字段缺失走兜底` @@ -331,6 +325,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ - **index(快照 + 融合)**:`快照已复制` / `index.md 已融合` / `快速路径下未执行` / `待处理(见备注)` - **topicMetadata(存量审计)**:`已补齐` / `待用户确认` / `快速路径下未执行`;列出新增 / 修正 / 删除的 topicId - **topic frontmatter**:`已自动补齐 N 个` / `已完整无需补齐` / `strict 校验失败` / `快速路径下未执行` +- **summary 初筛锚**:`已补写 N 个(已 kb build 同步 rule.summary)` / `已合规无需补写` / `快速路径下未执行` - **f2s-kb-upgrade SKILL**:`init 后无变化` / `已按新版从 2c 起重跑 N 轮(不再次 init)` / `快速路径下跳过该闭环` / `待确认` - **`projectRev` 回写**:`已写入项目 manifest(值=pkgRev)` / `快速路径下未执行` / `pkgRev=null 未动` - manifest-routing / matchers 分片:`已与模板对齐` / `已是最新` / `reset 覆盖` @@ -351,7 +346,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ ## 完成后自检 -1. 是否已做 **步骤 -1**:在进入步骤 0 前**已顺序前台执行 3 条探测**(`flow2spec --version` / `npm view ... version` / `npx` 可用性),并按 A/B/C 分支得出结论;仅在 B/C 时才**派独立子 agent**后台跑 `npm i -g @double-coding/flow2spec@latest`(不等待),A 分支**未派**任何升级动作;步骤 2 命令默认形态是否随分支选定(A→`flow2spec init`,B/C→`npx @latest init`);摘要中已写清分支与版本对比。 +1. 是否已做 **步骤 -1**:执行 `flow2spec version` 与 `flow2spec update --check`,记录 CLI/Core/Core Pinned/Template/Protocol;有更新时是否执行 `flow2spec update --cli` 联动刷新 CLI 与配套 Core,Template 更新是否按 A/B/C 选择当前 CLI 或 latest CLI;**A 分支停止前是否完成「项目侧对齐检查」**(manifest `version`/`pkgRev` 对比 + `kb check --strict`),未对齐时是否已转完整流程而非直接停止。 2. 是否已做 **步骤 0**:V1 已停止执行并告知用户处理方式、**现行库(V2+)** 正常进入 `init` 流程。 3. 是否在 **步骤 2 开始前** 记录了项目侧 `projectRev`(`projectRev`),并在 **步骤 2 的 `init` 之后** 重读 `pkgRev`、执行 **步骤 2c** 判定。 4. 是否在 **步骤 2 的 `init` 之后**重读过 **`f2s-kb-upgrade/SKILL.md`**:完整流程下有变化必须**按新版字面从步骤 2c 起重跑**(**不再次 init**);快速路径下可跳过该闭环(见「init 与技能自更新」「快速路径例外」)。 @@ -359,7 +354,7 @@ command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ 6. 是否明确标注增量 or reset 模式。 7. **完整流程时**:是否已处理旧主题文件清理与 `index/manifest` 引用修复(步骤 3)。 8. **完整流程时**:是否已执行 **步骤 3a**:审计 `topicMetadata`,确保无孤儿 key / 非法 primary / 非法 confidence;缺失旧主题已按证据补 `inferred` 或列为待确认。 -9. **完整流程时**:是否已执行 `flow2spec kb build --fix-topics` 或等价内部能力,并随后执行 `flow2spec kb check --strict`,确保存量 topic 已具备 `revision`。 +9. **完整流程时**:是否已执行 `flow2spec kb build --fix-topics` 或等价内部能力,并随后执行 `flow2spec kb check --strict`,确保存量 topic 已具备 `revision`;summary 质量 warning 是否已按步骤 3a.8 补写清零并 `kb build` 同步 `rule.summary`。 10. **完整流程时**:是否已执行 **步骤 3b**:**融合** `index.md`(**主题一览**节起至命中与执行前为项目维护区,其余同包版),并核对 `topicPaths`;**完整流程末尾**是否已**回写** 项目侧 `projectRev = pkgRev`(`pkgRev=null` 则保留原值)。 11. **快速路径时**:步骤 3 / 3a / 3b 是否真的跳过(未做无关扫描),摘要中明确标注「快速路径下未执行」。 12. 是否输出了 manifest 与关键路径校验结果。 diff --git a/packages/core/templates/en-US/knowledge/index.md b/packages/core/templates/en-US/knowledge/index.md index f31add5..f2b3a6f 100644 --- a/packages/core/templates/en-US/knowledge/index.md +++ b/packages/core/templates/en-US/knowledge/index.md @@ -10,7 +10,7 @@ The **machine-readable source of truth** is `.Knowledge/manifest-routing.json` p ## Recommended Reading Order 1. `.Knowledge/manifest-routing.json` (task routing, `topicPaths`, `topicDependencies`, `fallbackTopic`) -2. As needed: read `.Knowledge/matchers/.json` from `matcherPath` (`includeAny` keywords) +2. As needed: read `.Knowledge/matchers/.json` from `matcherPath` (`includeAny` / `includeAll` qualifiers and `excludeAny` / `excludeAll` vetoes) 3. As needed: this `index.md` (topic semantics and boundaries) 4. `.Knowledge/topics/.md` (execution constraints and flows) 5. As needed: `.Knowledge/stock-docs/`, `.Knowledge/req-docs/` @@ -37,7 +37,7 @@ Among these, **`implement-tech-design`**, **`f2s-doc-routing`**, **`config-prech ## Match and Execute (consistent with the unified entry) -- **Routing**: `taskToTopicRules` maps tasks to topic sets; **keywords** live in matcher-shard `includeAny`. +- **Routing**: `taskToTopicRules` maps tasks to topic sets; **keywords** live in matcher shards (`includeAny` / `includeAll` qualifier gates plus `excludeAny` / `excludeAll` veto gates; vetoes take precedence over exact `task` matches). - **Dependencies**: before using the main topic, read dependency topics according to `topicDependencies`. - **Fallback**: `fallbackTopic` points to a triage topic (such as `fallback-triage`) and is only low-confidence context. It **must not** be treated as a final hit for direct code changes. - **Execution chain**: `match → expand → verify → act`; `expand` must include dependency expansion and keep the next-highest candidate for validation. @@ -63,7 +63,7 @@ The routing manifest is maintained by `f2s-*` skill flows and does not depend on | Situation | What to do | | --- | --- | -| Docs exist but are not routed (1a) | Maintenance side: use `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` to supplement routing and `includeAny`. Execution side: use the triage topic to clarify task type; **do not** replace the manifest with full-repository scanning. | +| Docs exist but are not routed (1a) | Maintenance side: use `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` to supplement routing and matcher terms (`includeAny` / `includeAll` / `excludeAny` / `excludeAll`). Execution side: use the triage topic to clarify task type; **do not** replace the manifest with full-repository scanning. | | Routed but insufficient (1b) | Follow dependencies and next-highest candidates -> in `verify`, name the missing document; if still missing, ask the user for the path or add `req-docs`. | | Not in the KB (2) | Acknowledge the gap -> drill into code or ask the user to add requirement/spec documents. | | Repeated manifest reads waste tokens (2a) | Within the same task line, treat routing as a snapshot; read only the single matcher for the hit; do not enumerate the entire `matchers/` directory; do not repeatedly refresh `index.md` and routing against each other. | diff --git a/packages/core/templates/en-US/knowledge/topics/kb-routing-summary.md b/packages/core/templates/en-US/knowledge/topics/kb-routing-summary.md new file mode 100644 index 0000000..253be69 --- /dev/null +++ b/packages/core/templates/en-US/knowledge/topics/kb-routing-summary.md @@ -0,0 +1,64 @@ +--- +id: kb-routing-summary +revision: 2 +summary: "First-pass recall summary field and matcher four-field semantics (includeAny/includeAll/excludeAny/excludeAll)" +primary: feature +confidence: manual +--- +# Routing First-Pass `summary` Field and Matcher Four-Field Semantics + +## Scope + +First-pass hit rate, `taskToTopicRules[].summary` field semantics, `rule.summary` sync mechanism, routing miss triage, matcher-shard four-field decision rules (`includeAny` / `includeAll` / `excludeAny` / `excludeAll`), authoring and validation of `summary` / matcher entries. + +## Mechanism + +- `taskToTopicRules[].summary` is the always-loaded semantic anchor for first-pass recall; first-pass evidence = `task` + `summary` + topic id + dependencies + metadata (word lists such as `includeAny` are opened only after a hit). +- **The only handwritten source is topic frontmatter `summary`**; the manifest side is mechanically synced by `kb build` via `normalizeRoutingWithGraph`: multi-topic rules are joined with the full-width `;` separator, and if a topic has no summary the rule's existing value is preserved rather than cleared. +- The same helper covers `kb build` / `kb apply` / `kb status` (drift detection); hand-editing `rule.summary` in the manifest is flagged as routing drift. + +## Matcher-Shard Field Semantics + +Matcher shards (`.Knowledge/matchers/.json`) expose four fields consumed by `routing.match()`, forming a two-tier decision (veto gate → qualifier gate): + +- **Qualifier gate** (at least one must fire): + - `includeAny`: any phrase hits (OR) — the most common case. + - `includeAll`: all phrases hit (AND) — expresses compound / contextual constraints. +- **Veto gate** (any hit removes the rule — **outranks an exact `task` match**): + - `excludeAny`: any phrase hits (OR). + - `excludeAll`: all phrases hit (AND); a partial hit does NOT veto. + +Vetoes outrank exact `task` matches because they state "this request does not belong to this task domain", which is a stronger claim than an exact match. Legacy shards that use only `includeAny` remain unchanged. + +Authoring rules (when to use AND, when to add vetoes, and how they interact with the 12-entry threshold) live in `rules/f2s-topic-authoring` under "Matcher-Shard Field Semantics". Implementation: `packages/core/lib/routing.js:match()`; contract tests: `scripts/test-routing-semantics.js`. + +### Four-Field Population (Hand-Written On Demand, Not Engine-Generated) + +- `includeAll` / `excludeAny` / `excludeAll` have **no auto-population**. The engine only passes them through: `normalizeDeltaMatcher` preserves whatever the delta already carries when `kb apply` writes to disk, `kb build` never mutates matcher shards, and the default shards distributed by `flow2spec init` only contain `includeAny`. +- The **only path to a value** is: user encounters a mis-route or needs an AND compound term → the Agent applies the authoring rules from `f2s-topic-authoring` §3 and writes the field into the specific `matchers/.json` (or the user edits it directly). +- **No skill actively generates these three fields.** Even if a user description contains "exclude / avoid matching" wording, the Agent must observe, judge, and hand-write — this is not an automated flow. +- Positioning: **an escape hatch, not the default kit** — most topics need only `includeAny`. + +## Quality Checks (`kb check`) + +- **warning** (only affects the result under `--strict`): missing summary; placeholder text (`(路由摘要)` / `routing summary` / TODO / TBD / 待补充 / 占位 / equals topicId); over length (with CJK > 40 chars, English-only > 20 words). +- **issue** (hard fail): `rule.summary` is not a string. +- The CLI `kb check` text output prints the first 10 warning details. + +## Authoring and Legacy Repair + +- Writing conventions (soft 30 chars / hard 40 chars, responsibility + user-facing core nouns, prefer single-concept core words for `includeAny`, simulate real user questions before saving) are owned by `f2s-topic-authoring`'s "First-Pass Recall Rules" section; this topic does not restate them. +- Legacy repair follows the `f2s-kb-upgrade` full-flow steps 3a.7 / 3a.8: run `kb build --fix-topics` to backfill placeholder frontmatter → `kb check --strict` reports the summary warnings → the agent Reads each topic body and rewrites a real summary → `kb build` syncs it into `rule.summary`. + +## Boundaries + +- The `task` field keeps stable-id semantics (the kb engine merges rules by `task`) and does not carry recall semantics. +- Never hand-write `rule.summary` in the manifest — it is always regenerated by `kb build`. +- The verify miss-hit gate (when the topic body does not cover the core nouns in the user question, load the next-highest candidate too) is a unified-entry rule; see `rules/f2s-flow2spec-unified-entry`. + +## Implementation + +- Routing engine: `packages/core/lib/routing.js` (`match` — the four-field decision, score-pool merge, `fallbackTopic` fallback). +- Knowledge engine: `packages/core/lib/knowledgeEngine.js` (`deriveRoutingOverlayFromGraph.topicSummaries`, `normalizeRoutingWithGraph`, `validateKnowledgeGraph` summary checks, `normalizeDeltaMatcher` four-field normalization). +- CLI: `packages/cli/cli.js` — `kb check` warning detail output. +- Contract tests: `scripts/test-routing-semantics.js` (10 seam test cases). diff --git a/packages/core/templates/en-US/rules/f2s-flow2spec-unified-entry.md b/packages/core/templates/en-US/rules/f2s-flow2spec-unified-entry.md index fef0f9b..ab15647 100644 --- a/packages/core/templates/en-US/rules/f2s-flow2spec-unified-entry.md +++ b/packages/core/templates/en-US/rules/f2s-flow2spec-unified-entry.md @@ -29,7 +29,7 @@ Some environments create an **independent `git worktree`** or equivalent isolate ## Read Order (Mandatory) -1. First read `.Knowledge/manifest-routing.json`, prefer routing by `taskToTopicRules`; first-pass evidence is each rule's `task` name and `summary` (one-line intent digest); as needed, read the matcher shard from `matcherPath` to obtain `includeAny` keywords. If nothing matches, enter fallback recall. +1. First read `.Knowledge/manifest-routing.json`, prefer routing by `taskToTopicRules`; first-pass evidence is each rule's `task` name and `summary` (one-line intent digest); as needed, read the matcher shard from `matcherPath` for its word lists (`includeAny` / `includeAll` qualifiers and `excludeAny` / `excludeAll` vetoes; the veto gate is absolute and outranks exact `task` matches — see `topics/kb-routing-summary.md`). If nothing matches, enter fallback recall. - If the matched topic has dependencies in `topicDependencies`, read dependency topics first, then the main topic. - Routing manifests are maintained only by `f2s-*` skill flows and do not depend on extra CLI subcommands. 2. Read `.Knowledge/index.md` only as needed to confirm topic semantics and boundaries. @@ -60,7 +60,7 @@ Some environments create an **independent `git worktree`** or equivalent isolate - `taskToTopicRules[].matcherId`: stable matcher identifier; must match `id` inside the matcher shard. - `topicDependencies`: load dependency topics first after the main topic matches. - `topicMetadata`: topic-governance metadata. It only affects reading expectations, does not participate in matcher hits, does not decide whether a topic is read, and does not change execution mandatoryness. Execution mandatoryness always comes from explicit requirements in `AGENTS.md`, rules, skills, and topic bodies. When reading `topicMetadata[topicId].primary` / `tags`: `config` means focus on configuration items, switches, defaults, and initialization parameters; `policy` means prioritize mandatory/prohibited/gate/process constraints in the body; `feature` is background for implemented business/product capability; `module` is background for directories, packages, module boundaries, and engineering structure. `confidence` only allows `manual` / `inferred`; do not write metadata without clear classification evidence. -- `matcherPath(includeAny)`: task keyword matcher word list. +- `matcherPath(includeAny/includeAll/excludeAny/excludeAll)`: task keyword matcher lists (first two form the qualifier gate, last two the veto gate — see `topics/kb-routing-summary.md`). - `fallbackTopic`: must be read when neither task nor keyword matches, but is only a low-confidence fallback, not final execution basis. - `.Knowledge/manifest-routing.json + matcherPath shard files` are the machine-readable source of truth (keywords live only in `matchers/*.json`). - `.Knowledge/index.md` is not a machine-readable source of truth; it is only human-readable navigation and semantic-boundary validation. @@ -70,7 +70,7 @@ Some environments create an **independent `git worktree`** or equivalent isolate | Scenario | Response | | --- | --- | -| **1a Documents exist in the KB but routing is missing** | Use `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` to add `taskToTopicRules`, `matcherPath` shards, and `topicPaths`; expand `includeAny` to cover common user phrasing. Agent side: use `fallbackTopic` triage and state that "routing needs to be added"; do **not** replace configuration with full-repo file scanning. | +| **1a Documents exist in the KB but routing is missing** | Use `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` to add `taskToTopicRules`, `matcherPath` shards, and `topicPaths`; expand `includeAny` / `includeAll` to cover common user phrasing, and use `excludeAny` where mis-routes recur. Agent side: use `fallbackTopic` triage and state that "routing needs to be added"; do **not** replace configuration with full-repo file scanning. | | **1b Matched, but context is insufficient** | First `expand` (`topicDependencies` + secondary candidate), then `verify` and name which `stock-docs`/`req-docs` file or topic section is missing. If still insufficient, **ask the user for a document or path** instead of running an unconditional full cross-matcher search. **If the Agent needs to drill into source code**: first give the user a **visible gap note** (KB read, what is missing, which 1-2 files you plan to read); see the "gap gate" in **`f2s-knowledge-preflight`**. **Do not** run consecutive `Grep` calls or disorderly source exploration without that note. | | **2 The KB has no corresponding document** | After reading routing + matched matcher + related topics once, **explicitly acknowledge in the reply that the KB has no coverage**, then choose: drill into business code / ask the user to provide `req-docs` or a PRD. **Do not** repeatedly read lists to pretend "one more search will find it." Before source drilling, also satisfy the visible gap-note requirement in **`f2s-knowledge-preflight`**. | | **2a Repeated list reading wastes tokens** | Within the **same task line**, treat `manifest-routing.json` as a stable snapshot: rereading it in full requires a reason (for example, the user says routing/knowledge was updated by `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add`, or the manifest/matcher was **manually edited**). **Do not equate** running **`flow2spec init`** alone with "business KB was updated": `init` mainly writes the configuration root, fills missing directories, and aligns package-level routing structure. **stock-docs / req-docs, topic routing summaries, and matcher entries** are maintained by **`f2s-*` skill flows**. `init` writes rules into the configuration root **`rules/*`** (or equivalent extension) and writes Codex mirrors into **`.codex/topics/*.md`**. Read only the **single** `matcherPath` corresponding to the current rule; do not traverse the whole `matchers/` directory for enumeration. Open `index.md` only when topic semantics need checking; do not alternate between manifest and index to "refresh lists." | diff --git a/packages/core/templates/en-US/rules/f2s-topic-authoring.md b/packages/core/templates/en-US/rules/f2s-topic-authoring.md index 7473ff3..3ca771f 100644 --- a/packages/core/templates/en-US/rules/f2s-topic-authoring.md +++ b/packages/core/templates/en-US/rules/f2s-topic-authoring.md @@ -33,7 +33,7 @@ This rule is touched when any of the following is true: Every topic must include at least: 1. **Title and one-sentence intent** (one line stating "what this topic solves"); -2. **Applicable scenarios / trigger words** (semantically consistent with the corresponding `matchers/.json` `includeAny`); +2. **Applicable scenarios / trigger words** (semantically consistent with the corresponding `matchers/.json` `includeAny` / `includeAll`); 3. **Core rules / flow** (executable knowledge; steps must be reproducible by an Agent); 4. **Dependency declaration** (if dependencies exist in `topicDependencies`, the body must explicitly state "before executing, read dependency topic `` first"; use the first paragraph of `topics/f2s-req-plan.md` as a reference); 5. **Boundaries and prohibited items** (avoid expanding into neighboring topics); @@ -48,7 +48,34 @@ The topic frontmatter `summary` is synced by `flow2spec kb build` into `manifest - **Self-test (mandatory before writing to disk)**: simulate 2–3 natural user questions (imagine how users would ask, without looking at the word list) and check each hits at least one of `summary` or `includeAny`; revise on miss. - **`taskToTopicRules[].task`**: keep stable id semantics (the kb engine merges by `task`); do not stuff long phrases into it—recall semantics live in `summary`, and the manifest side is generated by `kb build`, never handwritten. -## 3. topicMetadata Decision Criteria +## 3. Matcher-Shard Field Semantics + +Matcher shards (`.Knowledge/matchers/.json`) expose four fields consumed by `routing.match()`. They form a two-tier decision (veto gate → qualifier gate): + +- **Qualifier gate** (at least one must fire for the rule to become a candidate): + - `includeAny`: any phrase hits (OR) — the most common case. + - `includeAll`: all phrases hit (AND) — expresses compound / contextual constraints. +- **Veto gate** (any hit kicks the rule out — **absolute, outranks an exact `task` match**): + - `excludeAny`: any phrase hits (OR). + - `excludeAll`: all phrases hit (AND); a partial hit does NOT veto. + +Authoring guidelines: + +1. **Default to only `includeAny`**: most topics need neither veto nor AND terms; a well-picked set of single-concept core words in `includeAny` is enough. +2. **Use `excludeAny` to eliminate mis-routes**: when your `includeAny` list keeps being hit in an unrelated scenario (e.g. "deploy" hit by "rehearsal deployment"), add `excludeAny: ["rehearsal", "preview"]` to precisely reject that scenario while **preserving recall** — much cheaper than pruning triggers. +3. **Use `includeAll` for compound intent**: reserve it for cases where **each word alone is too broad, but together they mean the intended thing** (e.g. "data" + "migration"); otherwise, two `includeAny` entries express it better. +4. **Use `excludeAll` for co-occurring vetoes**: reserve it for cases where a single word must NOT veto, but both together clearly do not belong to this task domain (e.g. "archive" + "decommission"); this is rare — most veto needs are covered by `excludeAny`. +5. **Veto outranks exact `task` match**: even if the caller passes `task: "deploy"`, an `excludeAny` hit still removes the rule. Vetoes state "this request is not in this task domain", which is a stronger statement than an exact match. **If you never want the caller's `task` argument to be overridden, do not add `excludeAny` to that rule.** +6. **Score pool merge**: `includeAll` hits join the same pool as `includeAny` hits; `score` still equals the top phrase score, and `confidence` tiers are unchanged (exact task = high, ≥ 30 = medium, < 30 = low). + +Threshold notes: + +- More than 12 `includeAny` entries usually signals an overly broad topic (see Section 6 splitting strategy); if `includeAll` compound entries help narrow scope, evaluate the threshold by phrase count instead of raw word count. +- Veto lists have no strict cap, but more than 5 entries usually means qualifiers were too broad — tighten the qualifier gate first. + +Implementation: `packages/core/lib/routing.js:match()`; contract tests: `scripts/test-routing-semantics.js` (10 cases covering the four-field decision matrix and its interaction with exact-task). + +## 4. topicMetadata Decision Criteria `topicMetadata` is governance metadata. It only affects inventory, audit, and reading expectations; it does not participate in matcher hits, does not decide whether a topic is read, and does not change execution mandatoryness. Execution mandatoryness comes from explicit requirements in `AGENTS.md`, rules, skills, and topic bodies. @@ -71,7 +98,7 @@ Decision rules: Prohibited: creating, renaming, or splitting topics solely for classification; duplicating classification blocks in topic markdown bodies or `index.md`. -## 4. topicDependencies Decision Criteria +## 5. topicDependencies Decision Criteria Let the current topic be A and the candidate dependency be B. **Declare `A -> B` if any of these four questions hit**: @@ -91,14 +118,14 @@ Let the current topic be A and the candidate dependency be B. **Declare `A -> B` **Decision timing**: after final drafts and new/modified topics are written, scan the body for other topic ids and rule filenames referenced in **backticks**, apply the four questions one by one, and write `manifest-routing.topicDependencies` on hit. Also write an explicit dependency declaration in the new topic body (see skeleton item 4). -## 5. Large-Feature Splitting Strategy +## 6. Large-Feature Splitting Strategy When a business feature is large, prefer a "main topic + subtopics" structure instead of one oversized topic. **When to split (soft constraints; evaluate splitting when any condition is met)**: - The corresponding stock-doc exceeds **300-500 lines**: evaluate splitting, but do not hard-block; -- matcher `includeAny` exceeds **12 entries**: signal that the topic is too broad; +- matcher `includeAny` exceeds **12 entries**: signal that the topic is too broad (if `includeAll` compound entries narrow the scope, evaluate the threshold by phrase count instead of raw word count — see section 3); - the topic body contains second-level headings for more than **3 unrelated responsibility domains**; - during a `f2s-kb-upgrade` audit, the same topic is repeatedly matched by several unrelated task types. @@ -110,10 +137,10 @@ When a business feature is large, prefer a "main topic + subtopics" structure in **Do not**: -- Do not use `topicDependencies` to express "overview -> detail" navigation relationships (see reverse exclusions in section 4). +- Do not use `topicDependencies` to express "overview -> detail" navigation relationships (see reverse exclusions in section 5). - Do not force-create subtopics solely for splitting. If a submodule will not be independently routed, a topic is unnecessary. -## 6. Whether a Rule Needs a Corresponding Topic +## 7. Whether a Rule Needs a Corresponding Topic Criterion: **will this rule be matched as user-task routing?** @@ -122,7 +149,7 @@ Criterion: **will this rule be matched as user-task routing?** Misconception: "Important rules should have topics." Importance is not the same as "matched by user routing"; let the consuming SKILL directly `Read rules/.*` in its body instead of going through manifest routing. -## 7. Disk-Write Ownership (Pointer) +## 8. Disk-Write Ownership (Pointer) Write-ownership constraints for `manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/topics/*.md` **are governed by `f2s-flow2spec-unified-entry` and the "hard write-ownership constraints" inside each SKILL**. This rule does not repeat them; in conflicts, follow the unified entry and the corresponding SKILL. @@ -131,7 +158,7 @@ Write-ownership constraints for `manifest-routing.json` / `.Knowledge/index.md` - Adding / modifying a topic or `topicDependencies` before reading this rule. - Creating, renaming, or splitting topics only to fill classification. - Writing duplicated metadata sections such as `## Concept Classification` in a topic body or `index.md`. -- Forcing "important rules" into `taskToTopicRules` (see section 6). +- Forcing "important rules" into `taskToTopicRules` (see section 7). - Using `topicDependencies` to express "information is related" (use `index.md` semantic boundaries + matcher keyword recall instead of dependency edges). - Writing transitive redundant edges or cycles in `topicDependencies`. - **Listing `.Knowledge/req-docs/*` files (clarifications / technical designs / SQL / PRDs) in a topic's "Long-form background / Detailed materials / Related materials / Long-form source / Reference documents" reference slot.** These slots may only point at `.Knowledge/stock-docs/*_终稿.md`; when no stock-doc exists yet, consolidate it first and then fill the slot. Short-sentence / inline evidence pointers are not covered by this prohibition. diff --git a/packages/core/templates/zh-CN/knowledge/index.md b/packages/core/templates/zh-CN/knowledge/index.md index bce6b9d..9e7c519 100644 --- a/packages/core/templates/zh-CN/knowledge/index.md +++ b/packages/core/templates/zh-CN/knowledge/index.md @@ -10,7 +10,7 @@ ## 推荐阅读顺序 1. `.Knowledge/manifest-routing.json`(任务路由、`topicPaths`、`topicDependencies`、`fallbackTopic`) -2. 按需:由 `matcherPath` 读取 `.Knowledge/matchers/.json`(`includeAny` 关键词) +2. 按需:由 `matcherPath` 读取 `.Knowledge/matchers/.json`(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词) 3. 按需:本 `index.md`(主题语义与边界) 4. `.Knowledge/topics/.md`(执行约束与流程) 5. 按需:`.Knowledge/stock-docs/`、`.Knowledge/req-docs/` @@ -37,7 +37,7 @@ ## 命中与执行(与统一入口一致) -- **路由**:`taskToTopicRules` 给出任务 → 主题集合;**关键词**在 matcher 分片的 `includeAny`。 +- **路由**:`taskToTopicRules` 给出任务 → 主题集合;**关键词**在 matcher 分片(`includeAny` / `includeAll` 资格门 + `excludeAny` / `excludeAll` 否决门;否决优先于 `task` 精确命中)。 - **依赖**:命中主主题前,按 `topicDependencies` 先读依赖主题。 - **兜底**:`fallbackTopic` 指向分诊主题(如 `fallback-triage`),仅低置信度上下文,**不得**当作最终命中直接改代码。 - **执行链**:`match → expand → verify → act`;`expand` 须含依赖展开,并保留次高候选做校验。 @@ -63,7 +63,7 @@ | 情况 | 你怎么做 | | --- | --- | -| 有文档但没配到(1a) | 维护侧:`f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补路由与 `includeAny`。执行侧:分诊主题澄清任务类型,**不**用全仓扫替代 manifest。 | +| 有文档但没配到(1a) | 维护侧:`f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补路由与 matcher 词表(`includeAny` / `includeAll` / `excludeAny` / `excludeAll`)。执行侧:分诊主题澄清任务类型,**不**用全仓扫替代 manifest。 | | 配到了但不够(1b) | 走依赖与次高候选 → `verify` 点名缺哪篇文档;仍缺则向用户要路径或补 `req-docs`。 | | 库里没有(2) | 承认缺口 → 代码下钻或请用户补需求/方案文档。 | | 反复读 manifest 费 token(2a) | 同一任务线内 routing 只当快照;只读命中项的单个 matcher;不遍历整个 `matchers/` 目录枚举;`index.md` 勿与 routing 循环互刷。 | diff --git a/packages/core/templates/zh-CN/knowledge/topics/kb-routing-summary.md b/packages/core/templates/zh-CN/knowledge/topics/kb-routing-summary.md new file mode 100644 index 0000000..e1b737f --- /dev/null +++ b/packages/core/templates/zh-CN/knowledge/topics/kb-routing-summary.md @@ -0,0 +1,64 @@ +--- +id: kb-routing-summary +revision: 2 +summary: "初筛 summary 与 matcher 分片 4 字段(资格/否决门)语义" +primary: feature +confidence: manual +--- +# 路由初筛 summary 字段与 matcher 4 字段 + +## 适用场景 + +路由初筛命中率、`taskToTopicRules[].summary` 字段语义、rule.summary 同步机制、路由 miss 排查、matcher 分片的 `includeAny` / `includeAll` / `excludeAny` / `excludeAll` 4 字段判定规则、summary/matcher 创作与校验问题。 + +## 机制 + +- `taskToTopicRules[].summary` 是初筛阶段的常驻语义锚;初筛证据 = `task` 名 + `summary` + topic id + 依赖 + metadata(`includeAny` 等词表仅在命中后打开的 matcher 分片中)。 +- **唯一手写源 = topic frontmatter `summary`**;manifest 侧由 `kb build` 经 `normalizeRoutingWithGraph` 机械同步:多 topic 规则按全角「;」拼接,topic 无 summary 时保留规则既有值不清空。 +- 同步点位复用:`kb build` / `kb apply` / `kb status`(drift 检测)三点位走同一函数;手改 manifest 的 `rule.summary` 会被判 routing drift。 + +## matcher 分片字段语义 + +matcher 分片(`.Knowledge/matchers/.json`)有 4 个字段参与路由匹配,按「否决门(恒胜) → 资格门(OR ∪ AND)」两级判定: + +- **资格门**(至少满足其一才进入候选): + - `includeAny`:任一短语命中(OR)——最常用 + - `includeAll`:所有短语全部命中(AND)——表达组合词 +- **否决门**(任一门命中则整条规则出局,**优先于 `task` 精确命中**): + - `excludeAny`:任一短语命中(OR) + - `excludeAll`:所有短语全部命中(AND)——半命中不否决 + +否决优先于 `task` 精确命中:排除词表达「此请求不属于该任务域」,语义上比精确命中更强。存量分片(仅有 `includeAny`)行为不变。 + +字段写作规范(何时用 AND / 什么时候上排除词 / 与 12 词阈值的关系)见 `rules/f2s-topic-authoring` 「matcher 分片字段语义」一节。实现:`packages/core/lib/routing.js:match()`;契约测试:`scripts/test-routing-semantics.js`。 + +### 4 字段填充机制(按需人写,引擎不生成) + +- `includeAll` / `excludeAny` / `excludeAll` **无自动填充**——引擎只做透传:`normalizeDeltaMatcher` 在 `kb apply` 写盘时保留 delta 已带字段,`kb build` 不动 matcher 分片,`flow2spec init` 分发的默认分片模板也只有 `includeAny`。 +- 赋值的**唯一路径**:用户遇到误路由 / AND 组合词需求 → Agent 依 `f2s-topic-authoring` §3 写作准则手写进对应 `matchers/.json`(或用户直接手工编辑)。 +- **无 skill 会主动生成这 3 个字段**——即便用户描述含"排除/避免命中"语义,也需 Agent 观察 + 判断 + 手写,不是自动流程。 +- 定位:**逃生舱,不是默认装备**——大部分主题只用 `includeAny` 就够。 + +## 质量校验(kb check) + +- **warning**(`--strict` 才影响结果):summary 缺失;占位(`(路由摘要)` / `routing summary` / TODO / TBD / 待补充 / 占位 / 与 topicId 相同);超长(含 CJK 时 > 40 字符,纯英文 > 20 词)。 +- **issue**(直接失败):`rule.summary` 非字符串。 +- CLI `kb check` 文本输出打印前 10 条 warning 明细。 + +## 创作与存量修复 + +- 写法规范(软 30 字 / 硬 40 字、职责 + 用户会问的核心名词、`includeAny` 单概念词优先、落盘前模拟问句自测)以 `f2s-topic-authoring`「初筛召回规范」为准,本 topic 不复述。 +- 存量修复走 `f2s-kb-upgrade` 完整流程步骤 3a.7 / 3a.8:`kb build --fix-topics` 补占位头部 → `kb check --strict` 报 summary warning → agent 逐个 Read 正文补写语义摘要 → `kb build` 同步进 `rule.summary`。 + +## 边界 + +- `task` 字段保持稳定 id 语义(kb 引擎按 `task` 合并规则),不承载召回语义。 +- 禁止手写 manifest 的 `rule.summary`,一律由 `kb build` 生成。 +- verify 错命中闸门(命中正文未覆盖问句核心名词时并读次高候选)属统一入口规则条文,见 `rules/f2s-flow2spec-unified-entry`。 + +## 实现位置 + +- 路由引擎:`packages/core/lib/routing.js`(`match` 函数的 4 字段判定 + 打分池合并 + `fallbackTopic` 兜底)。 +- 知识引擎:`packages/core/lib/knowledgeEngine.js`(`deriveRoutingOverlayFromGraph.topicSummaries`、`normalizeRoutingWithGraph`、`validateKnowledgeGraph` 的 summary 校验、`normalizeDeltaMatcher` 的 4 字段归一化)。 +- CLI:`packages/cli/cli.js` kb check 的 warning 明细输出。 +- 契约测试:`scripts/test-routing-semantics.js`(10 组 seam 测试)。 diff --git a/packages/core/templates/zh-CN/rules/f2s-flow2spec-unified-entry.md b/packages/core/templates/zh-CN/rules/f2s-flow2spec-unified-entry.md index 04006c2..f3feeca 100644 --- a/packages/core/templates/zh-CN/rules/f2s-flow2spec-unified-entry.md +++ b/packages/core/templates/zh-CN/rules/f2s-flow2spec-unified-entry.md @@ -29,7 +29,7 @@ alwaysApply: true ## 读取顺序(必须) -1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;初筛证据为每条规则的 `task` 名与 `summary`(一句话意图摘要);按需根据 `matcherPath` 读取 matcher 分片获取 `includeAny` 关键词;无法命中时进入补召回阶段。 +1. 先读 `.Knowledge/manifest-routing.json`,优先按 `taskToTopicRules` 路由;初筛证据为每条规则的 `task` 名与 `summary`(一句话意图摘要);按需根据 `matcherPath` 读取 matcher 分片获取词表(`includeAny` / `includeAll` 资格词、`excludeAny` / `excludeAll` 否决词;否决门恒胜,优先于 `task` 精确命中,详见 `topics/kb-routing-summary.md`);无法命中时进入补召回阶段。 - 若命中主题在 `topicDependencies` 中存在依赖,先读依赖主题,再读主主题。 - 路由清单仅通过 `f2s-*` 技能流程维护,不依赖额外 CLI 子命令。 2. `.Knowledge/index.md` 按需读取,仅用于确认主题语义与边界。 @@ -60,7 +60,7 @@ alwaysApply: true - `taskToTopicRules[].matcherId`:matcher 的稳定标识,需与 matcher 分片内 `id` 一致。 - `topicDependencies`:主主题命中后先加载依赖主题。 - `topicMetadata`:主题治理元数据,只影响阅读预期,不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性;执行强制性始终以 `AGENTS.md`、rules、skills 与 topic 正文中的明确要求为准。读到 `topicMetadata[topicId].primary` / `tags` 时:`config` 关注配置项、开关、默认值、初始化参数;`policy` 优先检查正文中的必须/禁止/门禁/流程约束;`feature` 作为已落地业务/产品能力背景;`module` 作为目录、包、模块边界与工程结构背景。`confidence` 仅允许 `manual` / `inferred`;无明确分类证据时不写 metadata。 -- `matcherPath(includeAny)`:任务关键词匹配词表。 +- `matcherPath(includeAny/includeAll/excludeAny/excludeAll)`:任务关键词匹配词表(前两者为资格门,后两者为否决门;详见 `topics/kb-routing-summary.md`)。 - `fallbackTopic`:任务与关键词都未命中时必须读取,但仅作低置信度兜底,不是最终执行依据。 - `.Knowledge/manifest-routing.json + matcherPath 分片文件` 是机读事实源(关键词仅在 `matchers/*.json`)。 - `.Knowledge/index.md` 不是机读事实源,仅作人读导航与语义边界校验。 @@ -70,7 +70,7 @@ alwaysApply: true | 情况 | 对策 | | --- | --- | -| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` 覆盖用户常用说法。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | +| **1a 库里有文档但未配路由** | 用 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 补 `taskToTopicRules`、`matcherPath` 分片、`topicPaths`;扩充 `includeAny` / `includeAll` 覆盖用户常用说法,必要时用 `excludeAny` 排除误路由。Agent 侧:走 `fallbackTopic` 分诊并提示「需补路由」,**不**靠全仓扫文件代替配置。 | | **1b 命中了但上下文不够** | 先 `expand`(`topicDependencies` + 次高候选),再 `verify` 点名缺哪份 `stock-docs`/`req-docs` 或哪段 topic;仍不足则 **向用户要文档或路径**,不要无门槛跨 matcher 全量补检索。**Agent 若需下钻源码**:须先对用户做**可见的缺口说明**(已读 KB、缺什么、拟读哪 1~2 个文件),见 **`f2s-knowledge-preflight`**「缺口闸门」;**禁止**无说明地连续 `Grep`/乱序探源。 | | **2 库里没有对应文档** | 一次读完 routing + 已命中 matcher + 相关 topic 后,在回复中 **明确承认知识库无覆盖**,再选:下钻业务代码 / 请用户补充 `req-docs` 或 PRD。**禁止**用反复读清单假装「再找一遍就会有」。**下钻源码前**同样须满足 **`f2s-knowledge-preflight`**「缺口闸门」的可见说明。 | | **2a 反复读清单耗 token** | **同一任务线内** `manifest-routing.json` 视为稳定快照:再次全文读取须说明理由(例如用户声明已通过 `f2s-kb-build` / `f2s-kb-sync` / `f2s-kb-add` 等更新路由或知识、或**手动编辑**了 manifest/matcher)。**勿将**仅执行 **`flow2spec init`** 等同于「业务知识库已更新」:`init` 以配置根落盘、目录补齐与包级路由结构对齐为主;**stock-docs / req-docs、topics 路由摘要、matchers 词条**由 **`f2s-*` 技能流程**维护;`init` 会把规则写入配置根 **`rules/*`**(或等价扩展名),并为 Codex 写入 **`.codex/topics/*.md`**。只读 **当前规则对应的单个** `matcherPath`;不要为枚举而遍历整个 `matchers/` 目录。`index.md` 仅在需核对主题语义时打开,禁止与 manifest 交替「刷清单」。 | diff --git a/packages/core/templates/zh-CN/rules/f2s-topic-authoring.md b/packages/core/templates/zh-CN/rules/f2s-topic-authoring.md index 4e975c1..2cae945 100644 --- a/packages/core/templates/zh-CN/rules/f2s-topic-authoring.md +++ b/packages/core/templates/zh-CN/rules/f2s-topic-authoring.md @@ -32,7 +32,7 @@ alwaysApply: false 每个 topic 至少包含: 1. **标题与一句话意图**(一行写清"该 topic 解决什么"); -2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` 语义一致); +2. **适用场景 / 触发词**(与对应 `matchers/.json` `includeAny` / `includeAll` 语义一致); 3. **核心规则 / 流程**(可执行知识;步骤须可由 Agent 复现); 4. **依赖声明**(若 `topicDependencies` 中存在依赖项,正文须显式写一句「执行前须先读依赖主题 ``」,参考 `topics/f2s-req-plan.md` 首段写法); 5. **边界与禁止项**(避免膨胀到隔壁 topic); @@ -47,7 +47,34 @@ topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest- - **自测(落盘前必做)**:模拟 2–3 个用户自然问句(凭空想“用户会怎么问”,不看词表),检查每句至少命中 `summary` 或 `includeAny` 之一;不命中则回改。 - **`taskToTopicRules[].task`**:保持稳定 id 语义(kb 引擎按 `task` 合并),不塞长短语;召回语义由 `summary` 承载,manifest 侧由 `kb build` 生成,不手写。 -## 3. topicMetadata 判定准则 +## 3. matcher 分片字段语义 + +matcher 分片(`.Knowledge/matchers/.json`)有 4 个字段参与 `routing.match()` 判定,按「否决门 → 资格门」两级: + +- **资格门**(至少满足其一即候选): + - `includeAny`:任一短语命中(OR)——最常用 + - `includeAll`:所有短语全部命中(AND)——表达组合词/上下文约束 +- **否决门**(任一门命中即整条规则出局,**恒胜,优先于 `task` 精确命中**): + - `excludeAny`:任一短语命中(OR) + - `excludeAll`:所有短语全部命中(AND);半命中不否决 + +写作准则: + +1. **默认只写 `includeAny`**:大部分主题不需要否决词/AND 词;泛化的单概念核心词交给 `includeAny` 即可。 +2. **用 `excludeAny` 排除误路由**:当同一批 `includeAny` 词在**另一场景**下反复被错命中(如 "部署" 词被 "预演部署" 误命中),用 `excludeAny: ["预演", "preview"]` 精确排除,**保留召回率**——比删触发词代价小得多。 +3. **用 `includeAll` 表达组合词**:仅当**两个词单独都太泛、但同时出现才有意义**时使用(如 "数据 + 迁移");否则拆成两条 `includeAny` 更好。 +4. **用 `excludeAll` 表达"仅当同时否定"**:仅当"任一词单独出现都不应否决,但两个词同时出现明确不属于本任务域"时使用(如 "归档 + 下线");这是罕见场景,大多数否决需求用 `excludeAny` 就够。 +5. **否决优先于 `task` 精确命中**:即使调用方显式传入 `task: "deploy"`,只要 `excludeAny` 命中,该规则仍然出局——排除词表达的是「此请求不属于该任务域」,语义强于精确命中。**若你不想让 `task` 参数被否决,不要给这条 rule 写 `excludeAny`**。 +6. **打分池合并**:`includeAll` 命中的短语与 `includeAny` 命中一起进入打分池,`score` 取最高短语分,不改变 `confidence` 分档规则(exact task=high、≥30=medium、<30=low)。 + +阈值提示: + +- `includeAny` 词表超过 12 个通常是主题过宽信号(见第 6 节拆分策略);若通过 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估。 +- 排除词无阈值约束,但写超过 5 条通常意味着资格词已过泛,应回头收紧资格门。 + +实现:`packages/core/lib/routing.js:match()`;契约测试:`scripts/test-routing-semantics.js`(10 组用例覆盖 4 字段判定门与 exact-task 交互)。 + +## 4. topicMetadata 判定准则 `topicMetadata` 是治理元数据,只影响盘点、审计和阅读预期;不参与 matcher 命中,不决定是否读取 topic,不改变执行强制性。执行强制性以 `AGENTS.md`、rules、skills 与 topic 正文明确要求为准。 @@ -70,7 +97,7 @@ topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest- 禁止:为了分类创建、重命名、拆分 topic;在 topic markdown 正文或 `index.md` 中重复写分类块。 -## 4. topicDependencies 判定准则 +## 5. topicDependencies 判定准则 设当前主题为 A、候选依赖为 B。**四问命中任一即声明 `A → B`**: @@ -90,14 +117,14 @@ topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest- **判定时机**:终稿与新 / 改 topic 落盘后,扫正文中**反引号引用的其他 topic id 与规则文件名**,逐个套四问;命中即写入 `manifest-routing.topicDependencies`,**并在新 topic 正文显式写依赖声明**(见骨架第 4 条)。 -## 5. 大功能拆分策略 +## 6. 大功能拆分策略 当一个业务功能体量较大时,推荐「主 topic + 子 topic」结构,而非单个大 topic。 **何时拆分(软约束,满足任一评估是否需拆)**: - 对应 stock-doc 超过 **300–500 行**:建议评估拆分,不强制阻断; -- matcher `includeAny` 超过 **12 个**:主题过宽信号; +- matcher `includeAny` 超过 **12 个**:主题过宽信号(若用 `includeAll` 组合词收敛后仍多,阈值按短语数而非词数评估,详见第 3 节); - topic 正文包含超过 **3 个不相干职责域**的二级标题; - `f2s-kb-upgrade` 审计时发现同一 topic 被多种不相干任务类型反复命中。 @@ -109,10 +136,10 @@ topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest- **不要做的事**: -- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 4 节反向排除); +- 不用 `topicDependencies` 表达"概述 → 详情"导航关系(见第 5 节反向排除); - 不为拆分而强行制造子 topic,若子模块本身不会被独立路由命中,不必建 topic。 -## 6. rule 是否需新建对应 topic +## 7. rule 是否需新建对应 topic 判据:**该 rule 是否会作为用户任务路由命中**。 @@ -121,7 +148,7 @@ topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest- 误区:「重要的规则就该有 topic」——重要不等于"用户路由命中";让消费方 SKILL 在正文里直接 `Read rules/.*` 全文即可,无需走 manifest 路由。 -## 7. 写盘权属(指针) +## 8. 写盘权属(指针) `manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/topics/*.md` 的写权约束**以 `f2s-flow2spec-unified-entry` 与各 SKILL 内「写权硬约束」为准**,本条不复述;遇分歧以统一入口与对应 SKILL 为准。 @@ -130,7 +157,7 @@ topic frontmatter 的 `summary` 会被 `flow2spec kb build` 同步进 `manifest- - 在未读本条的情况下新增 / 修改 topic 或 `topicDependencies`。 - 为补分类单独创建、重命名或拆分 topic。 - 在 topic 正文或 `index.md` 中写 `## 概念分类` 等 metadata 副本。 -- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 4 条)。 +- 把"重要的规则"硬塞进 `taskToTopicRules`(参见第 7 条)。 - 用 `topicDependencies` 表达"信息相关"(应通过 `index.md` 语义边界 + matcher 关键词补召回,而非依赖边)。 - 在 `topicDependencies` 中写传递冗余边或形成环。 - **在 topic 的「长文背景 / 详细资料 / 相关资料 / 长文来源 / 参考文档」等指向长文源的整节引用槽位里,列出 `.Knowledge/req-docs/*`**(含澄清 / 技术方案 / SQL / PRD)。此类槽位只允许指向 `.Knowledge/stock-docs/*_终稿.md`;无终稿时须先生成再回填。短句/佐证式的偶发点引不受此约束。 \ No newline at end of file