Harden dev-flow: pre-authorization marker, alternatives log, check script
- Record rejected answers in decision.md at the moment they are rejected - Allow explicit pre-authorized Build via a pre-authorized marker line - Land scripts/check-dev-flow.sh and derive devflow/index.md from archives - Add dev-flow process artifacts (decisions, prd) for open changes
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Decision: 年份筛选采用纯前端派生,不新增数据字段
|
||||
|
||||
## Problem
|
||||
|
||||
知识索引只能按关键词和标签过滤。当条目跨越多个月份和年份时,列表只会越来越长,而用户缺少一个**粗粒度**的收敛手段——想回看某一年沉淀了什么,只能靠肉眼扫或者记住关键词。
|
||||
|
||||
而"年份"这个维度其实**已经躺在数据里**了:每个条目的 `date` 字段(格式 `YYYYMMDD`)是渲染日期时的唯一来源。也就是说这不是一个需要新增信息的需求,而是一个**已经存在的信息没有暴露给用户**的问题。
|
||||
|
||||
## Decision
|
||||
|
||||
年份从 `ENTRIES[].date.slice(0,4)` 派生,**不新增数据字段**。
|
||||
|
||||
- 控件用原生 `<select id="year-filter">`,位置在搜索框下方、标签云上方,与搜索、标签同属一组合并过滤。
|
||||
- 选项从条目里**实际出现的**年份去重生成、倒序排列;只接受能匹配 `/^\d{4}$/` 的值。默认"全部年份"。
|
||||
- 过滤并入现有 `applyFilters()` 作为**单一过滤入口**,顺序为 `年份 → 标签 → 搜索`。
|
||||
- 修改在 `scripts/update-knowledge-index.sh` 的 HTML 模板里完成,再重建 `knowledge-index.html`。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**新增 `year` 字段到条目数据。** 冗余——`date` 已是唯一来源,派生成本为零,多一个字段就多一处可能不一致的地方。而且条目是 `knowledge_*/` 下的手写 markdown,新增字段意味着所有历史条目都要回填。
|
||||
|
||||
**引入第三方筛选组件(Select2 等)。** 为 4 行原生 `<select>` 代码引入依赖,与仓库"单文件静态 HTML、零依赖"的约束直接冲突。
|
||||
|
||||
**只修改生成后的 `knowledge-index.html`,不改生成脚本。** 更快,但 `knowledge-index.html` 是**生成产物**——下一次重建即丢功能,生成脚本才是真理源。这条在实现过程中确实被识别为"主要风险"(见 `design.md` 的架构审计一节),但**当时没有被记成一条备选**。它是一个真实的、有诱惑力的错误选项,所以补录在这里。
|
||||
|
||||
## Consequences
|
||||
|
||||
**得到**
|
||||
|
||||
- 零依赖、无数据迁移,没有新字段需要维护一致性。
|
||||
- 与既有搜索、标签过滤自然组合,因为三者走同一个 `applyFilters()` 入口。
|
||||
- 年份选项只列**实际存在**的年份,不会出现空年份。
|
||||
- 重建索引不会丢功能——修改落在生成脚本的模板里。
|
||||
|
||||
**代价**
|
||||
|
||||
- **只有年粒度**:不支持月份、日期范围、时间线视图或自定义排序。
|
||||
- `<select>` 选项数量随年份**线性增长**。以当前条目量完全可接受;多年后可能需要改成可搜索的下拉。
|
||||
- 过滤在前端全量进行。条目数量级显著变大时需要改为服务端或预建索引方案。
|
||||
|
||||
**已知限制**
|
||||
|
||||
- `date` 为空或不匹配 `YYYYMMDD` 的条目:不进入年份选项,在"全部年份"下仍显示,选中具体年份时隐藏。
|
||||
- 年份比较基于**字符串前四位**,不做真实日期解析——因此 `20260230` 这类非法日期会被当作 2026 年处理。这是有意的取舍:真实解析会引入日期库依赖,而收益只覆盖一个不会出现的输入。
|
||||
|
||||
## Verification
|
||||
|
||||
以下命令均在仓库根目录执行,路径为相对路径。
|
||||
|
||||
**只读(可直接跑,已在本次收尾时实测)**
|
||||
|
||||
- 控件同时存在于生成产物与模板两处
|
||||
`grep -c 'id="year-filter"' knowledge-index.html scripts/update-knowledge-index.sh`
|
||||
→ `knowledge-index.html: 1`,`scripts/update-knowledge-index.sh: 1`
|
||||
|
||||
- 年份过滤确实并入唯一入口 `applyFilters()`(函数体起始于第 217 行)
|
||||
`grep -n 'selectedYear' knowledge-index.html`
|
||||
→ `219`(读取控件值)、`228`(判断)、`229`(执行过滤)——三处都在函数体内
|
||||
|
||||
- 生成脚本语法完整(只做语法检查,不执行)
|
||||
`bash -n scripts/update-knowledge-index.sh` → `exit=0`
|
||||
|
||||
**有副作用(会重写 `knowledge-index.html`,故未在收尾时运行)**
|
||||
|
||||
- 重建后功能仍在
|
||||
`bash scripts/update-knowledge-index.sh`,然后复跑上面两条 `grep`
|
||||
|
||||
**人工**
|
||||
|
||||
选择 `2026` → 只显示 `date` 以 `2026` 开头的条目;再激活一个标签 → 两者取交集;选"全部年份" → 年份过滤解除,而标签仍然生效。
|
||||
@@ -0,0 +1,49 @@
|
||||
# Decision: 建立旧 devflow 档案的迁移规则
|
||||
|
||||
<!-- authorized: 2026-09-18 确认点1通过,用户对话授权("继续"),范围:单样本迁移+本change四件套 -->
|
||||
|
||||
## Problem
|
||||
|
||||
dev-flow 取消了 devflow 作为"人类档案层"的定位(2026-09-18 重设计),决策档案的唯一落点变为 `openspec/changes/<slug>/decision.md`。但历史遗留 9 个项目、37 个文件仍在 `devflow/projects/` 下,按四套并存的命名约定组织,与 OpenSpec 归档内容大量重复。
|
||||
|
||||
真实成本:同一次变更的事实存在两处权威(如 `add-year-filter` 的 design 双写),而最稀缺的内容——备选方案——全库仅 1/37 被记录。不迁移,则 dev-flow 的新真理源布局与旧档案层长期并存,"一个事实只有一个权威"持续被违反;全删,则丢失 ADR-0001 等真实决策资产。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Q: 全部删除 devflow/projects/? A: 不——ADR-0001(knowledge-index-panel)与部分 decisions.md 的关键取舍是真实决策资产,删了就丢。
|
||||
- Q: 全部原地保留、只加链接? A: 不——治标;活跃 change 缺 decision.md 的检查缺口仍在,重复权威仍在。
|
||||
- **只迁移值得留的,骨架删除,以单样本先行定规则。** 全量一次迁移风险不可控(37 文件四套约定);单样本验证规则后再批量。
|
||||
|
||||
## Decision
|
||||
|
||||
**以"寿命规则"处置旧 devflow 档案:跨变更活的并入新真理源,随变更死的不留;单样本先行,批量另立 change。**(2026-09-18 实际执行)
|
||||
|
||||
- 单样本 `2026-05-18-knowledge-index-panel`:ADR-0001 五节转译为 `openspec/changes/knowledge-index-panel/decision.md`(备选转 Q&A 形态,`migrated-from` 注释溯源);PRD 收存为 change 内 `prd.md`(冻结);glossary 引用同步更新
|
||||
- 首次迁移**不删除**源文件——删除等批量规则稳定后统一执行
|
||||
- 迁移规则五步(评估→转译→收存→改引用→暂不删)沉淀在 `design.md` 的 Migration Plan,批量迁移按此执行
|
||||
|
||||
计划与执行一致,无偏差。
|
||||
|
||||
## Consequences
|
||||
|
||||
**买到了**:
|
||||
- knowledge-index-panel 由检查 ✗ 转 ✓,决策资产进入新真理源布局
|
||||
- 迁移规则经一次真实执行验证,批量迁移(其余 8 项目)有了可引用的依据
|
||||
- dev-flow 全路径(Think→确认点1→Build→Close)首次完整运行,协议自举验证
|
||||
|
||||
**付出了**:
|
||||
- 旧 ADR-0001 与新 decision.md 双存在,直到批量清理——期间旧文件无入边(glossary 已改指新位置),漂移风险低但非零
|
||||
- PRD 中 Implementation Decisions 小节与 design.md 内容有重叠——PRD 按历史文档冻结收存,接受
|
||||
- 迁移是**补录**(retrofit):Alternatives 是从 ADR-0001 转译而非 grill 当场记录,形态合规但时机不理想——这正是它作为"历史迁移"而非"新变更"的本性
|
||||
|
||||
## Verification
|
||||
|
||||
**只读(可直接跑)**
|
||||
- `bash scripts/check-dev-flow.sh knowledge-index-panel` → 3 项全过,exit=0
|
||||
- `bash scripts/check-dev-flow.sh migrate-devflow-archives` → 3 项全过,exit=0
|
||||
- `grep -c 'decision.md' devflow/glossary/CONTEXT.md` → ≥1(引用已指向新位置)
|
||||
- `head -3 openspec/changes/knowledge-index-panel/decision.md` → 首行为 `# Decision: 目录名作为日期和标题的真理源`
|
||||
|
||||
**有副作用(会重写文件,收尾时不必跑)**
|
||||
- `bash scripts/check-dev-flow.sh --index`(重生成 devflow/index.md)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
## Context
|
||||
|
||||
dev-flow 重设计后,决策档案唯一落点为 `openspec/changes/<slug>/decision.md`。旧 `devflow/projects/`(9 项目/37 文件,四套命名约定并存)需按"寿命规则"处置:**跨变更活的留(并入 decision.md 或 glossary),随变更死的不留(骨架删除)**。
|
||||
|
||||
本 change 只执行单样本(`2026-05-18-knowledge-index-panel`),验证规则;批量迁移是后续独立 change。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
- knowledge-index-panel 获得合规的 decision.md(迁移自 ADR-0001,五节俱全)
|
||||
- PRD 独有内容(User Stories、测试决策、Out of Scope)不丢失
|
||||
- glossary 引用不因迁移产生死链
|
||||
- 迁移规则本身作为 decision 沉淀,可被批量迁移引用
|
||||
|
||||
**Non-Goals**
|
||||
- 不批量处理其余 8 个项目
|
||||
- 不删除 devflow/projects/ 任何文件(删除动作在批量规则稳定后)
|
||||
- 不处理 add-year-filter(已有 decision.md)与 sm-flow-execution-hardening(待其自身 Close)
|
||||
|
||||
## Decisions
|
||||
|
||||
| 决策 | 理由 | 备选 |
|
||||
|---|---|---|
|
||||
| ADR-0001 转译为 decision.md 而非原样复制 | 旧格式(背景/决策/替代方案/后果)→ 新五节骨架,Alternatives 转 Q&A 形态;`migrated-from` 注释保留溯源 | 原样复制(格式两套并存) |
|
||||
| PRD 收存为 change 内 `prd.md` | User Stories/测试决策/Out of Scope 是 proposal 没有的独有内容;v3.1 规则"复杂需求才留 PRD"在此适用 | 删除(丢失独有内容)/留在 devflow(违反唯一权威) |
|
||||
| 溯源用 HTML 注释而非正文小节 | 注释不渲染不干扰阅读,机器可查 | 正文"来源"小节(视觉噪音) |
|
||||
| 首次迁移不删源文件 | 删除是破坏性动作;规则未经批量验证前保守 | 迁移即删(不可逆风险) |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **devflow/projects 旧文件与新 decision.md 短暂双存在**(至批量清理):接受——单样本期间删除更危险;glossary 已指向新位置,旧 ADR 无入边
|
||||
- **PRD 中 Implementation Decisions 小节与 design.md 可能重复**:接受——PRD 是历史文档,冻结收存,不回改
|
||||
|
||||
## Migration Plan
|
||||
|
||||
单样本流程(批量迁移按此规则执行):
|
||||
|
||||
1. 评估旧项目文件:有独有决策/备选/取舍 → 并入对应 change 的 decision.md;纯骨架/与 OpenSpec 重复 → 标记待删
|
||||
2. 转译格式:旧四节 → 新五节,备选转 Q&A 形态,加 `migrated-from` 溯源注释
|
||||
3. PRD 等独有产物收存进 change 目录(冻结,不回改)
|
||||
4. glossary 等活文件中的路径引用同步更新
|
||||
5. 源文件暂不删,批量规则稳定后统一清理
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 其余 8 个项目中,`dev-flow-skill-evaluation` 的评估结论已沉淀进 workflow.md 演进史,其 todo.md 大概率可删——留批量时判断
|
||||
- `sm-flow-v3-1-upgrade` 等后期项目的 decisions.md 含真实取舍(术语裁决、产物瘦身决策),逐条评估是否值得并入 sm-flow 归档——批量时逐项目处置
|
||||
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
dev-flow 重设计(2026-09-18)取消了 devflow 作为独立档案层的定位:决策档案唯一落点为 `openspec/changes/<slug>/decision.md`,随归档冻结;devflow/ 收缩为 glossary + rejected + 派生 index。历史遗留 9 个项目、37 个文件需要按新布局处置,否则新旧两种真理源布局长期并存。
|
||||
|
||||
SuperBizAgent-java 实测(46 项目/180 文件)已给出处置口径:压缩 Q&A 与 ADR 段落并入对应归档的 decision.md,骨架文件删除。本仓库先以单样本(`2026-05-18-knowledge-index-panel`)验证规则,再定批量执行。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为 `openspec/changes/knowledge-index-panel/` 新建 `decision.md`:内容迁移自 `devflow/projects/2026-05-18-knowledge-index-panel/adr/0001-directory-name-as-truth-source.md`(五节俱全),标注迁移来源
|
||||
- `knowledge-index-panel-prd.md` 含独有内容(User Stories、测试决策),收存为 `openspec/changes/knowledge-index-panel/prd.md`
|
||||
- 更新 `devflow/glossary/CONTEXT.md` 中对 ADR-0001 旧路径的引用,指向新位置
|
||||
- 本 change(`migrate-devflow-archives`)自身按 dev-flow 协议执行,其 decision.md 记录迁移规则本身
|
||||
- 其余 8 个项目:本次不动(见 Non-Goals)
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不批量迁移其余 8 个项目——单样本验证规则后另行执行
|
||||
- 不删除 `devflow/projects/` 下任何现有文件——删除等批量规则稳定后统一处理
|
||||
- 不处理 3 个活跃 change 中另外两个(`add-year-filter` 已有 decision.md;`sm-flow-execution-hardening` 待其自身 Close)
|
||||
|
||||
## Impact
|
||||
|
||||
- `openspec/changes/knowledge-index-panel/` 新增 2 文件(decision.md、prd.md)
|
||||
- `devflow/glossary/CONTEXT.md` 改 1 处链接
|
||||
- `scripts/check-dev-flow.sh knowledge-index-panel` 应由 ✗ 转 ✓
|
||||
- 迁移规则沉淀在 `openspec/changes/migrate-devflow-archives/decision.md`,作为后续批量迁移的依据
|
||||
@@ -0,0 +1,11 @@
|
||||
# Tasks: migrate-devflow-archives
|
||||
|
||||
- [x] 1. Think:写 proposal.md + decision.md 的 Problem/Alternatives(迁移规则作为备选记录)
|
||||
- [x] 2. 确认点 1:汇报方案,获得授权并记录
|
||||
- [x] 3. 单样本:knowledge-index-panel/decision.md 迁移自 ADR-0001(五节转译,Q&A 备选,migrated-from 溯源)
|
||||
- [x] 4. 单样本:prd.md 收存进 change 目录
|
||||
- [x] 5. glossary/CONTEXT.md 的 ADR-0001 引用更新为新路径
|
||||
- [x] 6. design.md(迁移规则细节)与 tasks.md 落盘
|
||||
- [x] 7. Close:补齐本 change decision.md 的 Decision/Consequences/Verification
|
||||
- [x] 8. 验证:`check-dev-flow.sh knowledge-index-panel` 与 `check-dev-flow.sh migrate-devflow-archives` 均通过;`--index` 重生成
|
||||
- [x] 9. 确认点 2:汇报产物与剩余风险,用户确认归档(2026-09-18,"继续")
|
||||
@@ -0,0 +1,42 @@
|
||||
# Decision: 目录名作为日期和标题的真理源
|
||||
|
||||
<!-- migrated-from: devflow/projects/2026-05-18-knowledge-index-panel/adr/0001-directory-name-as-truth-source.md (2026-09-18, change: migrate-devflow-archives) -->
|
||||
|
||||
## Problem
|
||||
|
||||
知识吸收器输出目录遵循 `knowledge_YYYYMMDD_Title/` 命名约定。目录内的 `.md` 文件包含 YAML frontmatter,其中也有 `date`/`created` 和 `title` 字段,但实际使用中发现字段名不一致(`date` vs `created`)且格式有差异。
|
||||
|
||||
索引重建脚本需要确定日期和标题的权威来源——两个来源都存在且都可能被读到时,不指定权威就必然漂移。
|
||||
|
||||
## Decision
|
||||
|
||||
**日期和短标识符 (slug) 的真理源是目录名。显示标题、tags、author 的真理源是 YAML frontmatter。**
|
||||
|
||||
- 日期从目录名的 `YYYYMMDD` 部分提取
|
||||
- 短标识符从目录名的 `Slug` 部分提取(用于文件路径构建,不用于显示)
|
||||
- 显示标题从 YAML frontmatter 的 `title` 字段提取
|
||||
- `tags` 和 `author` 从 YAML frontmatter 提取
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- Q: YAML 字段全优先? A: 不——字段名不一致(date vs created),脚本要处理变体,漂移点更多。
|
||||
- Q: 目录名全优先? A: 不——标题中的语义信息在下划线转空格后丢失(如 `mattpocock_skills` → `mattpocock skills`,实际标题更长更精确)。
|
||||
|
||||
## Consequences
|
||||
|
||||
**买到了**:
|
||||
- 索引脚本不需要处理日期字段名变体(date vs created)
|
||||
- 目录名是可见的、可审计的——与 `ls` 输出完全一致
|
||||
- bash 用 glob 匹配 `knowledge_*` 目录,天然获取了日期和 slug
|
||||
- 显示标题从 YAML 取,支持完整的、精确的标题文本(含标点、中文)
|
||||
|
||||
**付出了**:
|
||||
- 索引脚本需要解析 YAML frontmatter(复杂度比仅取目录名高)
|
||||
- 如果目录被重命名,slug 会变化但显示标题不受影响(可接受的行为)
|
||||
- 知识吸收器 skill 必须严格遵守 `knowledge_YYYYMMDD_Slug` 命名约定
|
||||
|
||||
## Verification
|
||||
|
||||
**只读(可直接跑)**
|
||||
- `ls knowledge/entries/ | head -3` → 目录名均形如 `knowledge_YYYYMMDD_Slug`
|
||||
- `grep -n 'date' scripts/update-knowledge-index.sh | head -5` → 日期提取自目录名而非 YAML
|
||||
@@ -0,0 +1,57 @@
|
||||
# PRD: 知识库索引面板 (Knowledge Index Panel)
|
||||
|
||||
## Problem Statement
|
||||
|
||||
用户每次用 `/knowledge-absorber` 学习后,会在项目根目录生成 `knowledge_YYYYMMDD_Title/` 文件夹。目前有 2 个知识条目,预计每月增长 5-10 个。所有条目散落在根目录,没有跨条目的导航或搜索能力。想找之前学过的内容只能手动翻目录,搜索成本随条目数线性上升。
|
||||
|
||||
## Solution
|
||||
|
||||
在项目根目录生成一个 **纯静态的 `knowledge-index.html`**,浏览器直接打开即可使用。它能自动发现所有 knowledge 条目,提供标签关联和全文搜索。不需要任何服务器、数据库或外部依赖。
|
||||
|
||||
## User Stories
|
||||
|
||||
1. As a 学习者, I want to see all my knowledge entries on one page with dates, so that I can quickly find what I studied and when
|
||||
2. As a 学习者, I want to search across all knowledge entries by keyword, so that I can find "that thing about Redis" in 2 seconds instead of manually opening 10 folders
|
||||
3. As a 学习者, I want to click a tag like "Claude Code" and see all related entries, so that I can review interconnected topics
|
||||
4. As a 学习者, I want the index to auto-discover new knowledge entries, so that I don't need to manually update anything after running `/knowledge-absorber`
|
||||
5. As a 学习者, I want the page to work offline by just double-clicking the HTML file, so that I can use it without internet or any setup
|
||||
6. As a 学习者, I want to see which entries are related to each other via shared tags, so that I can discover connections between topics I've studied
|
||||
7. As a 学习者, I want the search to highlight matching text, so that I can see at a glance why an entry matched my query
|
||||
8. As a 学习者, I want to see a count of entries per tag, so that I know which topics I've studied most
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
- **Single HTML file**: All CSS and JS inlined in `knowledge-index.html`. No external files or CDN dependencies
|
||||
- **Manifest-based discovery**: A JavaScript array inside the HTML lists all known knowledge directory names. Browser security sandbox prevents dynamic directory listing via `fetch()`
|
||||
- **YAML frontmatter parsing**: Regex-based extraction from `.md` files. Simple key:value parsing only — no full YAML spec support
|
||||
- **Client-side search**: All `.md` content loaded into memory at page init. Real-time filtering with `String.includes()`, case-insensitive. `<mark>` tags for highlighting
|
||||
- **Tag system**: In-memory `Map<tag, entry[]>` built at load time. Click to filter, click again to deselect
|
||||
- **Related Content**: Computed from shared tags, displayed per-entry
|
||||
- **No pagination**: Designed for < 50 entries. Full list rendered at once
|
||||
- **UTF-8 encoding**: All files assumed UTF-8. `fetch()` handles encoding detection
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
- **What makes a good test**: Test external behavior — "user types keyword → correct entries appear" — not internal state
|
||||
- **Test with real data**: Use existing 2 knowledge entries (`knowledge_20260417_Waza`, `knowledge_20260518_mattpocock_skills`) as test fixtures
|
||||
- **Edge cases to verify**:
|
||||
- Missing directory (manifest lists a name that doesn't exist) → skip gracefully
|
||||
- Malformed YAML → display directory name as fallback
|
||||
- Empty tags array → no tag badges rendered
|
||||
- No search results → show "没有找到相关条目" message
|
||||
- Browser CORS when opened via `file://` protocol → document the workaround
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Google Drive / cloud sync
|
||||
- Editing knowledge entries
|
||||
- Full YAML 1.2 spec compliance
|
||||
- Pagination or virtual scrolling
|
||||
- Fuse.js or other fuzzy search libraries (keep it zero-dependency)
|
||||
- Dark mode toggle (can add later, not in v1)
|
||||
|
||||
## Further Notes
|
||||
|
||||
- The manifest maintenance problem (adding new entries) can be solved in a future iteration by modifying the `/knowledge-absorber` skill to auto-append to the manifest array
|
||||
- The `file://` protocol CORS limitation on Chrome means users may need to serve via `python -m http.server` or similar for full functionality
|
||||
- Tag counts provide a natural "learning heatmap" — most-studied topics bubble to the top
|
||||
Reference in New Issue
Block a user