openmoneta-dev-kit 1.13.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/hooks/enforce-docs-first.sh +34 -0
- package/hooks/inject-process-context.sh +3 -2
- package/hooks/verify-completion.sh +35 -0
- package/opencode/AGENTS.md.tpl +4 -3
- package/opencode/plugins/openmoneta-guard.ts +94 -2
- package/package.json +1 -1
- package/scripts/init-project.sh +38 -0
- package/skills/decision-recorder/SKILL.md +61 -0
- package/skills/module-architect/SKILL.md +10 -0
- package/skills/plan-writer/SKILL.md +12 -0
- package/skills/requirement-analysis/SKILL.md +11 -0
- package/skills/systematic-debugging/SKILL.md +10 -0
- package/templates/AGENTS.md.tpl +9 -6
- package/templates/adr.md.tpl +37 -0
- package/templates/decisions-INDEX.md.tpl +28 -0
- package/templates/docs-INDEX.md.tpl +7 -0
- package/templates/plans-INDEX.md.tpl +1 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2.0.0
|
|
@@ -101,6 +101,8 @@ INDEX_FILE="$WORKSPACE/docs/INDEX.md"
|
|
|
101
101
|
[[ -f "$INDEX_FILE" ]] || allow
|
|
102
102
|
|
|
103
103
|
MARKER="$WORKSPACE/.cursor/.docs-index-read"
|
|
104
|
+
DECISIONS_INDEX="$WORKSPACE/docs/decisions/INDEX.md"
|
|
105
|
+
DECISIONS_MARKER="$WORKSPACE/.cursor/.decisions-read"
|
|
104
106
|
|
|
105
107
|
case "$PATHS" in
|
|
106
108
|
*"docs/INDEX.md"*)
|
|
@@ -110,6 +112,15 @@ case "$PATHS" in
|
|
|
110
112
|
;;
|
|
111
113
|
esac
|
|
112
114
|
|
|
115
|
+
# Đọc bất kỳ file nào trong docs/decisions/ → set marker decisions-read (vẫn allow vì là docs).
|
|
116
|
+
case "$PATHS" in
|
|
117
|
+
*"docs/decisions/"*)
|
|
118
|
+
mkdir -p "$WORKSPACE/.cursor"
|
|
119
|
+
touch "$DECISIONS_MARKER"
|
|
120
|
+
allow
|
|
121
|
+
;;
|
|
122
|
+
esac
|
|
123
|
+
|
|
113
124
|
case "$PATHS" in
|
|
114
125
|
*"docs/"*|*"plans/"*|*".cursor/"*|*".github/"*|*".vscode/"*) allow ;;
|
|
115
126
|
*"AGENTS.md"*|*"README"*|*"CHANGELOG"*|*"LICENSE"*) allow ;;
|
|
@@ -164,6 +175,29 @@ LƯU Ý:
|
|
|
164
175
|
|
|
165
176
|
(Hook này chỉ chạy 1 lần / session. Sau khi đọc docs/INDEX.md, marker .cursor/.docs-index-read được set và hook sẽ allow mọi tool call source-code khác trong session.)"
|
|
166
177
|
fi
|
|
178
|
+
|
|
179
|
+
# === Decisions-read gate (Decision Memory / chống flip-flop kiến trúc) ===
|
|
180
|
+
# Chỉ enforce khi project CÓ docs/decisions/INDEX.md (project chưa có quyết định nào thì bỏ qua → giữ Lean).
|
|
181
|
+
if [[ -f "$DECISIONS_INDEX" && ! -f "$DECISIONS_MARKER" ]]; then
|
|
182
|
+
deny "Bạn đang $TOOL_NAME source code TRƯỚC khi đọc docs/decisions/ (trí nhớ quyết định kiến trúc). Vi phạm Bước 1 (skill requirement-analysis 1.3b).
|
|
183
|
+
|
|
184
|
+
DEBUG:
|
|
185
|
+
- Tool: $TOOL_NAME
|
|
186
|
+
- docs/decisions/INDEX.md: $DECISIONS_INDEX
|
|
187
|
+
- Marker expected: $DECISIONS_MARKER
|
|
188
|
+
- Tool input paths/patterns: $PATHS
|
|
189
|
+
|
|
190
|
+
VÌ SAO ENFORCE:
|
|
191
|
+
- Project này có quyết định kiến trúc đã ghi (ADR). Sửa code mà không đọc lịch sử quyết định = nguy cơ flip-flop kiến trúc (A1 → B1 → A1) làm loạn code.
|
|
192
|
+
- ADR ghi: kiến trúc đang chọn, vì sao, phương án nào đã bị loại + root cause, hạn chế đã biết.
|
|
193
|
+
|
|
194
|
+
HÀNH ĐỘNG NGAY:
|
|
195
|
+
→ Read $DECISIONS_INDEX (set marker), tìm ADR liên quan tới module bạn sắp đụng, đọc ADR đó nếu định đổi kiến trúc, sau đó retry $TOOL_NAME.
|
|
196
|
+
|
|
197
|
+
LƯU Ý:
|
|
198
|
+
- Muốn ĐỔI/ĐẢO kiến trúc đã Accepted → phải supersede tường minh (skill decision-recorder), KHÔNG revert lặng lẽ.
|
|
199
|
+
- Marker chỉ cần set 1 lần/session (auto-clean ở sessionStart)."
|
|
200
|
+
fi
|
|
167
201
|
fi
|
|
168
202
|
|
|
169
203
|
allow
|
|
@@ -27,11 +27,12 @@ register_project_cursor() {
|
|
|
27
27
|
echo "$abs" >> "$reg" 2>/dev/null || true
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
# Reset docs-index marker mỗi session (force re-read INDEX.md)
|
|
30
|
+
# Reset docs-index + decisions marker mỗi session (force re-read INDEX.md + decisions/)
|
|
31
31
|
if command -v jq >/dev/null 2>&1; then
|
|
32
32
|
WORKSPACE_FROM_INPUT=$(echo "$INPUT_JSON" | jq -r '.workspace_roots[0] // ""' 2>/dev/null || echo "")
|
|
33
33
|
if [[ -n "$WORKSPACE_FROM_INPUT" && -d "$WORKSPACE_FROM_INPUT/.cursor" ]]; then
|
|
34
34
|
rm -f "$WORKSPACE_FROM_INPUT/.cursor/.docs-index-read" 2>/dev/null || true
|
|
35
|
+
rm -f "$WORKSPACE_FROM_INPUT/.cursor/.decisions-read" 2>/dev/null || true
|
|
35
36
|
fi
|
|
36
37
|
register_project_cursor "$WORKSPACE_FROM_INPUT" 2>/dev/null || true
|
|
37
38
|
fi
|
|
@@ -50,7 +51,7 @@ SUMMARY='# OpenMoneta Dev Kit v1.7.0 — Quy trình 6 bước (Adaptive Planning
|
|
|
50
51
|
6. Safe Push: khi user yêu cầu push, PHẢI sync remote ngay trước push; KHÔNG force-push shared branch.
|
|
51
52
|
|
|
52
53
|
## 6 bước
|
|
53
|
-
- **B1 Phân tích** (skill `requirement-analysis`): Read INDEX → match Token Routing → đọc README module candidate → đọc source → đặt câu hỏi critical để làm rõ yêu cầu, phân tích mọi trường hợp, phản biện và đề xuất phương án tối ưu nếu có (hoặc khai báo skip lý do).
|
|
54
|
+
- **B1 Phân tích** (skill `requirement-analysis`): Read INDEX → match Token Routing → đọc README module candidate → đọc source → đặt câu hỏi critical để làm rõ yêu cầu, phân tích mọi trường hợp, phản biện và đề xuất phương án tối ưu nếu có (hoặc khai báo skip lý do). Nếu có `docs/decisions/INDEX.md`: PHẢI đọc ADR liên quan trước khi đổi kiến trúc (chống flip-flop A1→B1→A1); muốn đảo kiến trúc đã chốt → supersede tường minh (skill `decision-recorder`).
|
|
54
55
|
- **B2 Thiết kế** (skill `module-architect`): SRP 1 module 1 trách nhiệm. Feature mới có concept riêng → module mới (KHÔNG nhét utils/common/shared). Existing project: backfill `docs/modules/<slug>/README.md` 3 sections nếu thiếu. Module mới → thêm keyword vào Token Routing.
|
|
55
56
|
- **B3 Adaptive Plan** (skill `plan-writer`): task nhỏ/rõ → không cần plan. Task lớn/rủi ro/mơ hồ → tạo repo plan `Status: Draft`, trình user review, chỉ code sau khi user approve và đổi `Status: In Progress`.
|
|
56
57
|
- **B4 Triển khai**: nếu có plan active → chỉ edit file trong scope plan. Không plan → chỉ sửa đúng yêu cầu; nếu vượt ngưỡng/rủi ro → dừng tạo plan Draft.
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
# 7. Cross-module sprawl >3 module → WARN
|
|
8
8
|
# 8. Token-aware reading marker tồn tại → WARN nếu thiếu
|
|
9
9
|
# 9. Nếu có plan: section "## Hiểu yêu cầu" có nội dung không trống → BLOCK (audit clarify)
|
|
10
|
+
# 10. ADR supersede integrity: ADR mới "Supersedes: X" thì ADR X phải "Superseded by" → BLOCK
|
|
11
|
+
# 11. Plan khai báo "## Quyết định kiến trúc" (đổi/đảo) nhưng không sinh/sửa ADR → BLOCK
|
|
10
12
|
#
|
|
11
13
|
# Đã bỏ (so với v1.4.x):
|
|
12
14
|
# - Check 2 (test result file) — test giờ on-demand
|
|
@@ -134,6 +136,39 @@ if [[ -f "$WORKSPACE/docs/INDEX.md" && ! -f "$WORKSPACE/.cursor/.docs-index-read
|
|
|
134
136
|
WARNINGS+=("⚠️ Session có code change nhưng marker .cursor/.docs-index-read không tồn tại — AI có thể đã skip Bước 1 (Read docs/INDEX.md). Verify hook 'enforce-docs-first' đang chạy: cat ~/.cursor/hooks.json | jq '.hooks.preToolUse'. Nếu hook không chạy → restart Cursor sau khi cài v1.5.0.")
|
|
135
137
|
fi
|
|
136
138
|
|
|
139
|
+
# === Check 10/11: Decision Memory (ADR) integrity — chống flip-flop kiến trúc ===
|
|
140
|
+
CHANGED_DECISIONS=$(jq -r '.changes[] | .path' "$CHANGES_FILE" 2>/dev/null | grep -E '^docs/decisions/.*\.md$' | grep -v 'docs/decisions/INDEX.md' || true)
|
|
141
|
+
|
|
142
|
+
# Check 10: ADR mới/sửa khai báo "Supersedes: ADR-NNNN" thì ADR đích phải đã đổi sang "Superseded by"
|
|
143
|
+
if [[ -n "$CHANGED_DECISIONS" ]]; then
|
|
144
|
+
while IFS= read -r rel_adr; do
|
|
145
|
+
[[ -z "$rel_adr" ]] && continue
|
|
146
|
+
ADR_FILE="$WORKSPACE/$rel_adr"
|
|
147
|
+
[[ -f "$ADR_FILE" ]] || continue
|
|
148
|
+
SUPERSEDES=$(grep -ioE 'supersedes\*{0,2}:?[[:space:]]*ADR-[0-9]+' "$ADR_FILE" 2>/dev/null | grep -oE 'ADR-[0-9]+' | head -1 || true)
|
|
149
|
+
if [[ -n "$SUPERSEDES" ]]; then
|
|
150
|
+
NUM=$(echo "$SUPERSEDES" | grep -oE '[0-9]+')
|
|
151
|
+
TARGET=$(ls "$WORKSPACE/docs/decisions/${NUM}-"*.md 2>/dev/null | head -1 || true)
|
|
152
|
+
if [[ -z "$TARGET" ]]; then
|
|
153
|
+
ISSUES+=("❌ ADR '$(basename "$ADR_FILE")' khai báo 'Supersedes: $SUPERSEDES' nhưng không tìm thấy docs/decisions/${NUM}-*.md.")
|
|
154
|
+
elif ! grep -qiE 'superseded by' "$TARGET" 2>/dev/null; then
|
|
155
|
+
ISSUES+=("❌ ADR cũ '$(basename "$TARGET")' bị supersede bởi '$(basename "$ADR_FILE")' nhưng chưa đổi Status sang 'Superseded by ADR-...'. Cập nhật để tránh 2 ADR Accepted xung đột (gây flip-flop). Xem skill decision-recorder.")
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
done <<< "$CHANGED_DECISIONS"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Check 11: plan trong session khai báo đổi/đảo kiến trúc nhưng KHÔNG sinh/sửa ADR
|
|
162
|
+
for plan in $ALL_RECENT_PLANS; do
|
|
163
|
+
[[ -z "$plan" || ! -f "$plan" ]] && continue
|
|
164
|
+
if grep -qE "^##[[:space:]]+Quyết định kiến trúc" "$plan" 2>/dev/null; then
|
|
165
|
+
DECL=$(awk '/^##[[:space:]]+Quyết định kiến trúc/{flag=1; next} flag && /^##[[:space:]]/{exit} flag {print}' "$plan" | grep -iE 'supersede|đổi kiến trúc|revert|đảo ngược' || true)
|
|
166
|
+
if [[ -n "$DECL" && -z "$CHANGED_DECISIONS" ]]; then
|
|
167
|
+
ISSUES+=("❌ Plan '$(basename "$plan")' có '## Quyết định kiến trúc' khai báo đổi/đảo kiến trúc nhưng KHÔNG có docs/decisions/*.md được tạo/cập nhật. Tạo/supersede ADR (skill decision-recorder) trước khi đóng session.")
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
done
|
|
171
|
+
|
|
137
172
|
# === Output ===
|
|
138
173
|
WARN_STR=""
|
|
139
174
|
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
|
package/opencode/AGENTS.md.tpl
CHANGED
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
|
|
14
14
|
## Quy trình 6 bước
|
|
15
15
|
|
|
16
|
-
- **B1 Phân tích** (skill `requirement-analysis`): Read `docs/INDEX.md` → match Token Routing → đọc README module liên quan → đọc source → hỏi clarify nếu yêu cầu chưa rõ (ghi vào `## Hiểu yêu cầu` của plan).
|
|
16
|
+
- **B1 Phân tích** (skill `requirement-analysis`): Read `docs/INDEX.md` → match Token Routing → đọc README module liên quan → đọc source → hỏi clarify nếu yêu cầu chưa rõ (ghi vào `## Hiểu yêu cầu` của plan). **Nếu có `docs/decisions/INDEX.md`: PHẢI đọc nó + ADR liên quan trước khi đổi kiến trúc** (plugin guard chặn read source nếu chưa đọc `docs/decisions/`). Muốn đảo kiến trúc đã chốt → supersede tường minh (skill `decision-recorder`), chống flip-flop A1→B1→A1.
|
|
17
17
|
- **B2 Thiết kế** (skill `module-architect`): SRP 1 module 1 trách nhiệm. Feature có concept riêng → module mới (KHÔNG nhét utils/common/shared). **Module có source nhưng chưa có doc → BẮT BUỘC backfill `docs/modules/<slug>/README.md` 3 sections (Trách nhiệm + Public API + Dependencies)** trước khi sang B4. Module mới → thêm keyword vào Token Routing của `docs/INDEX.md`.
|
|
18
|
-
- **B3 Adaptive Plan** (skill `plan-writer`): task nhỏ/rõ → không cần plan. Task lớn/rủi ro/mơ hồ → tạo repo plan `Status: Draft`, trình user review, chỉ code sau khi user approve và đổi `Status: In Progress`.
|
|
18
|
+
- **B3 Adaptive Plan** (skill `plan-writer`): task nhỏ/rõ → không cần plan. Task lớn/rủi ro/mơ hồ → tạo repo plan `Status: Draft`, trình user review, chỉ code sau khi user approve và đổi `Status: In Progress`. Plan đổi/đảo kiến trúc → thêm section `## Quyết định kiến trúc` (`Supersede ADR-NNNN` + lý do root cause cũ hết đúng).
|
|
19
19
|
- **B4 Triển khai**: có plan active → chỉ edit file trong scope plan; không plan → chỉ sửa đúng yêu cầu. Mỗi sub-task xong → tick `- [x]`.
|
|
20
20
|
- **B5 Update doc + close plan** — **mandatory closing checklist** (plugin guard `event:session.idle` sẽ re-prompt nếu thiếu):
|
|
21
21
|
- [ ] Sync `docs/modules/<slug>/README.md` cho mọi module bị sửa: **Trách nhiệm + Public API + Dependencies**.
|
|
22
22
|
- [ ] Module mới đã có trong bảng "Modules hiện có" + "Token Routing" của `docs/INDEX.md`.
|
|
23
|
+
- [ ] Nếu session đổi kiến trúc → tạo/supersede ADR trong `docs/decisions/` + link vào README module + cập nhật `docs/decisions/INDEX.md` (skill `decision-recorder`).
|
|
23
24
|
- [ ] Plan → `Status: Done` + tick mọi checkbox.
|
|
24
25
|
- [ ] **AUTO-ARCHIVE**: `git mv plans/<file>.md plans/archive/<file>.md` + update `plans/INDEX.md` (Active → Archived).
|
|
25
26
|
- **B6 Pre-push Sync + Safe Push** (skill `safe-push`, CONDITIONAL): chỉ khi user yêu cầu push. `git fetch` + rebase trước push, conflict logic → hỏi user, `git push` thường, KHÔNG `--force` shared branch.
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
- OpenMoneta skills được cài vào `~/.config/opencode/skills/*/SKILL.md` và agent nên load bằng skill tool khi cần.
|
|
32
33
|
- **Bảo trì doc dự án cũ** (ON-DEMAND): khi user yêu cầu tạo/refresh docs còn thiếu hoặc lỗi thời, chạy `bash ~/.config/opencode/scripts/docs-audit.sh` để soi rồi load skill `docs-maintenance` (backfill MISSING + refresh STALE + sync `docs/INDEX.md`).
|
|
33
34
|
- OpenMoneta subagents được cài vào `~/.config/opencode/agents/`.
|
|
34
|
-
- OpenCode plugin guard trong `~/.config/opencode/plugins/openmoneta-guard.ts` enforce workflow tương đương Cursor hooks: `tool.execute.before` (docs-first + plan gate), `tool.execute.after` (track changes), và `event:session.idle` (verify B2/B5: module README
|
|
35
|
+
- OpenCode plugin guard trong `~/.config/opencode/plugins/openmoneta-guard.ts` enforce workflow tương đương Cursor hooks: `tool.execute.before` (docs-first + decisions-read gate + plan gate), `tool.execute.after` (track changes), và `event:session.idle` (verify B2/B5 + ADR integrity: module README, close plan, supersede ADR, re-prompt nhắc hoàn tất với loop guard ≤4 lần).
|
|
35
36
|
|
|
36
37
|
## Khi Bắt Đầu Task
|
|
37
38
|
|
|
@@ -375,6 +375,54 @@ function buildVerifyIssues(root: string): string[] {
|
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
// Check 10/11: Decision Memory (ADR) integrity — chống flip-flop kiến trúc
|
|
379
|
+
const changedDecisions = paths.filter(
|
|
380
|
+
(p) => /^docs\/decisions\/.*\.md$/.test(p) && p !== "docs/decisions/INDEX.md",
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
// Check 10: ADR mới "Supersedes: ADR-NNNN" thì ADR đích phải đã "Superseded by"
|
|
384
|
+
for (const rel of changedDecisions) {
|
|
385
|
+
const abs = path.join(root, rel)
|
|
386
|
+
if (!fs.existsSync(abs)) continue
|
|
387
|
+
const match = fs.readFileSync(abs, "utf8").match(/supersedes\*{0,2}:?\s*ADR-(\d+)/i)
|
|
388
|
+
if (!match) continue
|
|
389
|
+
const num = match[1]
|
|
390
|
+
const decisionsDir = path.join(root, "docs", "decisions")
|
|
391
|
+
const target = fs.existsSync(decisionsDir)
|
|
392
|
+
? fs.readdirSync(decisionsDir).find((f) => f.startsWith(`${num}-`) && f.endsWith(".md"))
|
|
393
|
+
: undefined
|
|
394
|
+
if (!target) {
|
|
395
|
+
issues.push(
|
|
396
|
+
`❌ ADR '${path.basename(abs)}' khai báo 'Supersedes: ADR-${num}' nhưng không tìm thấy docs/decisions/${num}-*.md.`,
|
|
397
|
+
)
|
|
398
|
+
} else if (!/superseded by/i.test(fs.readFileSync(path.join(decisionsDir, target), "utf8"))) {
|
|
399
|
+
issues.push(
|
|
400
|
+
`❌ ADR cũ '${target}' bị supersede bởi '${path.basename(abs)}' nhưng chưa đổi Status sang 'Superseded by ADR-...'. Cập nhật để tránh 2 ADR Accepted xung đột (flip-flop). Xem skill decision-recorder.`,
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Check 11: plan khai báo "## Quyết định kiến trúc" (đổi/đảo) nhưng không sinh/sửa ADR
|
|
406
|
+
for (const plan of planSet) {
|
|
407
|
+
if (!fs.existsSync(plan)) continue
|
|
408
|
+
const lines = fs.readFileSync(plan, "utf8").split(/\r?\n/)
|
|
409
|
+
let inSection = false
|
|
410
|
+
let declared = false
|
|
411
|
+
for (const line of lines) {
|
|
412
|
+
if (/^##\s+Quyết định kiến trúc/.test(line)) {
|
|
413
|
+
inSection = true
|
|
414
|
+
continue
|
|
415
|
+
}
|
|
416
|
+
if (inSection && /^##\s+/.test(line)) break
|
|
417
|
+
if (inSection && /supersede|đổi kiến trúc|revert|đảo ngược/i.test(line)) declared = true
|
|
418
|
+
}
|
|
419
|
+
if (declared && changedDecisions.length === 0) {
|
|
420
|
+
issues.push(
|
|
421
|
+
`❌ Plan '${path.basename(plan)}' có '## Quyết định kiến trúc' khai báo đổi/đảo kiến trúc nhưng KHÔNG có docs/decisions/*.md được tạo/cập nhật. Tạo/supersede ADR (skill decision-recorder) trước khi kết thúc.`,
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
378
426
|
return issues
|
|
379
427
|
}
|
|
380
428
|
|
|
@@ -450,8 +498,10 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
450
498
|
const client = ctx.client
|
|
451
499
|
const root = projectRoot(ctx)
|
|
452
500
|
const marker = path.join(root, ".cursor", ".docs-index-read")
|
|
501
|
+
const decisionsMarker = path.join(root, ".cursor", ".decisions-read")
|
|
453
502
|
const guardLoadedMarker = path.join(root, ".cursor", ".openmoneta-guard-loaded.json")
|
|
454
503
|
const docsIndex = path.join(root, "docs", "INDEX.md")
|
|
504
|
+
const decisionsIndex = path.join(root, "docs", "decisions", "INDEX.md")
|
|
455
505
|
const pendingEdits = new Map<string, string[]>()
|
|
456
506
|
|
|
457
507
|
fs.mkdirSync(path.dirname(marker), { recursive: true })
|
|
@@ -461,7 +511,7 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
461
511
|
{
|
|
462
512
|
loaded_at: new Date().toISOString(),
|
|
463
513
|
root,
|
|
464
|
-
version: "
|
|
514
|
+
version: "2.0.0",
|
|
465
515
|
load_count: globalState[globalKey],
|
|
466
516
|
},
|
|
467
517
|
null,
|
|
@@ -473,10 +523,13 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
473
523
|
registerProjectOpenCode(root)
|
|
474
524
|
|
|
475
525
|
// OpenCode does not have Cursor's sessionStart hook, so reset the docs-first
|
|
476
|
-
//
|
|
526
|
+
// + decisions-read markers when the plugin is loaded for a new OpenCode process/session.
|
|
477
527
|
if (fs.existsSync(marker)) {
|
|
478
528
|
fs.rmSync(marker, { force: true })
|
|
479
529
|
}
|
|
530
|
+
if (fs.existsSync(decisionsMarker)) {
|
|
531
|
+
fs.rmSync(decisionsMarker, { force: true })
|
|
532
|
+
}
|
|
480
533
|
|
|
481
534
|
return {
|
|
482
535
|
"tool.execute.before": async (
|
|
@@ -493,6 +546,11 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
493
546
|
fs.writeFileSync(marker, new Date().toISOString())
|
|
494
547
|
return
|
|
495
548
|
}
|
|
549
|
+
if (target.includes("docs/decisions/")) {
|
|
550
|
+
fs.mkdirSync(path.dirname(decisionsMarker), { recursive: true })
|
|
551
|
+
fs.writeFileSync(decisionsMarker, new Date().toISOString())
|
|
552
|
+
return
|
|
553
|
+
}
|
|
496
554
|
if (shouldCheckRead(tool, target) && !fs.existsSync(marker)) {
|
|
497
555
|
fail(
|
|
498
556
|
[
|
|
@@ -505,6 +563,20 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
505
563
|
].join("\n"),
|
|
506
564
|
)
|
|
507
565
|
}
|
|
566
|
+
if (shouldCheckRead(tool, target) && fs.existsSync(decisionsIndex) && !fs.existsSync(decisionsMarker)) {
|
|
567
|
+
fail(
|
|
568
|
+
[
|
|
569
|
+
"Bạn đang đọc/search source code trước khi đọc `docs/decisions/` (trí nhớ quyết định kiến trúc).",
|
|
570
|
+
"",
|
|
571
|
+
"Project này có ADR. Sửa code mà không đọc lịch sử quyết định = nguy cơ flip-flop kiến trúc (A1 → B1 → A1).",
|
|
572
|
+
"",
|
|
573
|
+
"Quy trình đúng:",
|
|
574
|
+
"1. Read `docs/decisions/INDEX.md`.",
|
|
575
|
+
"2. Đọc ADR liên quan tới module nếu định đổi kiến trúc.",
|
|
576
|
+
"3. Muốn đảo kiến trúc đã chốt → supersede tường minh (skill decision-recorder).",
|
|
577
|
+
].join("\n"),
|
|
578
|
+
)
|
|
579
|
+
}
|
|
508
580
|
}
|
|
509
581
|
|
|
510
582
|
if (isBashTool(tool) && fs.existsSync(docsIndex)) {
|
|
@@ -514,6 +586,11 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
514
586
|
fs.writeFileSync(marker, new Date().toISOString())
|
|
515
587
|
return
|
|
516
588
|
}
|
|
589
|
+
if (command.includes("docs/decisions")) {
|
|
590
|
+
fs.mkdirSync(path.dirname(decisionsMarker), { recursive: true })
|
|
591
|
+
fs.writeFileSync(decisionsMarker, new Date().toISOString())
|
|
592
|
+
return
|
|
593
|
+
}
|
|
517
594
|
if (isSourceSearchCommand(command) && !fs.existsSync(marker)) {
|
|
518
595
|
fail(
|
|
519
596
|
[
|
|
@@ -528,6 +605,21 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
528
605
|
].join("\n"),
|
|
529
606
|
)
|
|
530
607
|
}
|
|
608
|
+
if (
|
|
609
|
+
isSourceSearchCommand(command) &&
|
|
610
|
+
fs.existsSync(decisionsIndex) &&
|
|
611
|
+
!fs.existsSync(decisionsMarker)
|
|
612
|
+
) {
|
|
613
|
+
fail(
|
|
614
|
+
[
|
|
615
|
+
"Bạn đang chạy shell command search source code trước khi đọc `docs/decisions/` (trí nhớ quyết định kiến trúc).",
|
|
616
|
+
"",
|
|
617
|
+
`Command bị chặn: ${command}`,
|
|
618
|
+
"",
|
|
619
|
+
"Read `docs/decisions/INDEX.md` trước (chống flip-flop kiến trúc), rồi mới search source.",
|
|
620
|
+
].join("\n"),
|
|
621
|
+
)
|
|
622
|
+
}
|
|
531
623
|
}
|
|
532
624
|
|
|
533
625
|
if (isEditTool(tool)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openmoneta-dev-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "OpenMoneta Dev Kit — Biến Cursor IDE / OpenCode thành team developer hoàn chỉnh với quy trình 6 bước, adaptive planning, hooks enforcement, và token-aware doc routing",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cursor",
|
package/scripts/init-project.sh
CHANGED
|
@@ -337,6 +337,40 @@ sync_plans_index() {
|
|
|
337
337
|
UPDATED+=("sync: $dst (giữ ACTIVE PLANS / PLANS EXTRA / ARCHIVED PLANS / PLANS NOTES)")
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
sync_decisions_index() {
|
|
341
|
+
local src="$TEMPLATES/decisions-INDEX.md.tpl"
|
|
342
|
+
local dst="docs/decisions/INDEX.md"
|
|
343
|
+
|
|
344
|
+
# Template mới (v2.0.0+). Nếu install home cũ chưa có → bỏ qua êm.
|
|
345
|
+
[[ -f "$src" ]] || return 0
|
|
346
|
+
|
|
347
|
+
if [[ ! -f "$dst" ]]; then
|
|
348
|
+
render_template "$src" "$dst"
|
|
349
|
+
CREATED+=("file: $dst")
|
|
350
|
+
return
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
local rendered content
|
|
354
|
+
rendered=$(mktemp)
|
|
355
|
+
render_template "$src" "$rendered"
|
|
356
|
+
|
|
357
|
+
content=$(mktemp)
|
|
358
|
+
if extract_block "$dst" "PROJECT DECISIONS" "$content" || extract_section_table "$dst" "## Quyết định hiện có" "$content"; then
|
|
359
|
+
replace_block "$rendered" "PROJECT DECISIONS" "$content"
|
|
360
|
+
fi
|
|
361
|
+
rm -f "$content"
|
|
362
|
+
|
|
363
|
+
if cmp -s "$rendered" "$dst"; then
|
|
364
|
+
SKIPPED+=("file: $dst (đã sync, không đổi)")
|
|
365
|
+
rm -f "$rendered"
|
|
366
|
+
return
|
|
367
|
+
fi
|
|
368
|
+
|
|
369
|
+
backup_before_sync "$dst"
|
|
370
|
+
mv "$rendered" "$dst"
|
|
371
|
+
UPDATED+=("sync: $dst (giữ PROJECT DECISIONS)")
|
|
372
|
+
}
|
|
373
|
+
|
|
340
374
|
sync_opencode_project_guard() {
|
|
341
375
|
# Guard is now global-only (~/.config/opencode/plugins/).
|
|
342
376
|
# Just clean up any old project-level plugins from pre-v1.9 migration.
|
|
@@ -347,6 +381,7 @@ sync_opencode_project_guard() {
|
|
|
347
381
|
echo "==> Tạo thư mục tối thiểu..."
|
|
348
382
|
ensure_dir "docs"
|
|
349
383
|
ensure_dir "docs/modules"
|
|
384
|
+
ensure_dir "docs/decisions"
|
|
350
385
|
ensure_dir "plans"
|
|
351
386
|
ensure_dir "plans/archive"
|
|
352
387
|
ensure_dir ".cursor"
|
|
@@ -356,6 +391,7 @@ echo "==> Sync managed templates..."
|
|
|
356
391
|
|
|
357
392
|
sync_agents_md
|
|
358
393
|
sync_docs_index
|
|
394
|
+
sync_decisions_index
|
|
359
395
|
sync_plans_index
|
|
360
396
|
sync_opencode_project_guard
|
|
361
397
|
|
|
@@ -365,7 +401,9 @@ GITIGNORE=".gitignore"
|
|
|
365
401
|
PATTERNS=(
|
|
366
402
|
".cursor/.session-changes.json"
|
|
367
403
|
".cursor/.docs-index-read"
|
|
404
|
+
".cursor/.decisions-read"
|
|
368
405
|
".cursor/.openmoneta-guard-loaded.json"
|
|
406
|
+
".cursor/.openmoneta-verify-loop.json"
|
|
369
407
|
".cursor/.changelog-baseline"
|
|
370
408
|
".cursor/.last-test-result"
|
|
371
409
|
".cursor/.ui-session.json"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: decision-recorder
|
|
3
|
+
description: "Dùng khi ra/đổi/đảo một quyết định KIẾN TRÚC (chọn giữa ≥2 phương án, đổi cách triển khai một tính năng, hoặc muốn revert kiến trúc đã có). Ghi ADR vào docs/decisions/ để chống flip-flop kiến trúc xuyên session. KHÔNG dùng cho task code thường."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Decision Recorder (ADR)
|
|
7
|
+
|
|
8
|
+
Lớp **Decision Memory** giải quyết vòng lặp phá code: `A1 lỗi → session đổi sang B1 → B1 lỗi nhỏ → session mới (quên hết) đưa về A1 → lỗi cũ tái diễn`. ADR lưu **WHY + WHY-NOT + root cause** để mọi session sau hiểu vì sao kiến trúc hiện tại được chọn.
|
|
9
|
+
|
|
10
|
+
## Iron Law
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
KHÔNG ĐẢO NGƯỢC một kiến trúc đã Accepted (LOCKED) khi CHƯA:
|
|
14
|
+
(1) đọc ADR đang khóa của nó, VÀ
|
|
15
|
+
(2) supersede tường minh (ADR mới + chứng minh root cause cũ KHÔNG còn đúng).
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Vi phạm chữ = vi phạm tinh thần.** Đổi kiến trúc "cho nhanh" rồi định ghi ADR sau = sai. Phải đọc + supersede TRƯỚC.
|
|
19
|
+
|
|
20
|
+
## Khi nào TẠO ADR
|
|
21
|
+
|
|
22
|
+
- Chọn giữa ≥2 phương án kiến trúc có trade-off thật (state management, data flow, sync vs async, pattern...).
|
|
23
|
+
- Đổi cách triển khai một tính năng đang chạy (vd: polling → websocket).
|
|
24
|
+
- `systematic-debugging` xác nhận một approach hỏng do bản chất kiến trúc (không phải bug vặt).
|
|
25
|
+
- Đảo ngược / thay thế một quyết định cũ.
|
|
26
|
+
|
|
27
|
+
## Khi nào KHÔNG tạo ADR (tránh token bloat)
|
|
28
|
+
|
|
29
|
+
- Bug fix, thêm field, đổi text/CSS, rename, refactor nội bộ không đổi kiến trúc.
|
|
30
|
+
- Task nhỏ thường ngày. ADR chỉ dành cho **quyết định kiến trúc đáng nhớ**.
|
|
31
|
+
|
|
32
|
+
## Cách tạo ADR mới
|
|
33
|
+
|
|
34
|
+
1. Đọc `docs/decisions/INDEX.md` để biết số ADR lớn nhất hiện có → `NNNN` mới = max + 1 (4 chữ số).
|
|
35
|
+
2. Copy template `~/.cursor/templates/adr.md.tpl` (hoặc `~/.config/opencode/templates/adr.md.tpl`) → `docs/decisions/NNNN-<slug>.md`.
|
|
36
|
+
3. Điền 5 section: Context, Decision, **Rejected alternatives (kèm root cause)**, Consequences (known limitations), Failed approaches log.
|
|
37
|
+
4. Điền field: `Status: Accepted (LOCKED)`, `Date`, `Module(s)`, `Plan nguồn` (link plan archive nếu có — đây là **tầng chi tiết**), `Supersedes` (nếu thay ADR cũ).
|
|
38
|
+
5. Cập nhật bảng trong `docs/decisions/INDEX.md`.
|
|
39
|
+
6. Link ADR vào `docs/modules/<slug>/README.md` section "## Quyết định kiến trúc (ADR)".
|
|
40
|
+
|
|
41
|
+
## Quy trình SUPERSEDE (đảo/đổi quyết định cũ) — BẮT BUỘC
|
|
42
|
+
|
|
43
|
+
1. Đọc ADR cũ (`Accepted (LOCKED)`) + phần **Rejected alternatives** + **Failed approaches log** của nó.
|
|
44
|
+
2. Tự hỏi: root cause khiến phương án cũ bị loại có còn đúng không? Nếu CÒN đúng → **KHÔNG revert** (đây chính là cái bẫy flip-flop).
|
|
45
|
+
3. Nếu thật sự cần đổi: tạo ADR mới với `Supersedes: ADR-XXXX` + chứng minh trong section Context vì sao root cause cũ hết hiệu lực.
|
|
46
|
+
4. Đổi ADR cũ sang `Status: Superseded by ADR-NNNN` (giữ nguyên nội dung — không xóa, là lịch sử).
|
|
47
|
+
5. Plan tương ứng phải có section "## Quyết định kiến trúc" ghi rõ supersede (xem skill `plan-writer`).
|
|
48
|
+
|
|
49
|
+
## Token-lean (guideline, KHÔNG enforce cứng)
|
|
50
|
+
|
|
51
|
+
- Soft target **~40 dòng/ADR**, viết gạch đầu dòng. Quy tắc vàng: **"viết đủ để session sau không lặp lại sai lầm, không hơn"**.
|
|
52
|
+
- **Miễn giới hạn** cho phần root cause chi tiết + "Failed approaches log" (giữ sắc thái quan trọng).
|
|
53
|
+
- Cấu trúc 2 tầng: ADR = tóm tắt (luôn nạp); `Plan nguồn` (`plans/archive/<file>.md`) = chi tiết (nạp khi cần). KHÔNG tạo file `*-detail.md` riêng.
|
|
54
|
+
|
|
55
|
+
## Red Flags — DỪNG
|
|
56
|
+
|
|
57
|
+
- Định đổi kiến trúc mà chưa đọc ADR đang khóa của module.
|
|
58
|
+
- Định revert về phương án từng bị loại mà không kiểm tra root cause cũ.
|
|
59
|
+
- Tạo ADR mới `Accepted` nhưng KHÔNG đổi ADR cũ sang `Superseded by` (để 2 ADR Accepted xung đột — hook `verify-completion` Check 10 sẽ chặn).
|
|
60
|
+
- ADR phình dài vì văn xuôi thừa (không phải vì root cause).
|
|
61
|
+
- Tạo ADR cho task code thường (lạm dụng → token bloat).
|
|
@@ -228,9 +228,18 @@ Quy tắc maintenance:
|
|
|
228
228
|
- Có `import` mới từ module khác? → thêm.
|
|
229
229
|
- Có `import` bị xóa? → xóa.
|
|
230
230
|
5. Nếu module **mới được backfill** ở Bước 2.2 → đảm bảo `docs/INDEX.md` đã có trong bảng "Modules hiện có" + Token Routing.
|
|
231
|
+
6. **Nếu session ra/đổi quyết định kiến trúc cho module này** → tạo/cập nhật ADR (skill `decision-recorder`) trong `docs/decisions/` + thêm/sync section "## Quyết định kiến trúc (ADR)" trong README module (list các ADR-NNNN liên quan) + cập nhật `docs/decisions/INDEX.md`. KHÔNG bắt buộc cho task không đổi kiến trúc.
|
|
231
232
|
|
|
232
233
|
> KHÔNG cần update `changelog.md` cấp module — v1.5.0 bỏ enforcement này. Lịch sử dùng `git log` thay.
|
|
233
234
|
|
|
235
|
+
**Section ADR trong README module** (chỉ thêm khi module có quyết định kiến trúc):
|
|
236
|
+
|
|
237
|
+
```markdown
|
|
238
|
+
## Quyết định kiến trúc (ADR)
|
|
239
|
+
|
|
240
|
+
- [ADR-0003](../../decisions/0003-state-via-store.md) — dùng store tập trung thay vì props drilling (Accepted).
|
|
241
|
+
```
|
|
242
|
+
|
|
234
243
|
### Anti-patterns
|
|
235
244
|
|
|
236
245
|
- ❌ Copy nguyên block code vào README (sẽ stale).
|
|
@@ -243,6 +252,7 @@ Quy tắc maintenance:
|
|
|
243
252
|
|
|
244
253
|
- [ ] Module README đã sync với public API thực tế (nếu API đổi).
|
|
245
254
|
- [ ] Module mới được tạo → đã thêm vào bảng "Modules hiện có" + Token Routing trong `docs/INDEX.md`.
|
|
255
|
+
- [ ] Nếu session đổi kiến trúc → ADR đã tạo/cập nhật trong `docs/decisions/` + link vào README module + bảng `docs/decisions/INDEX.md` (xem skill `decision-recorder`).
|
|
246
256
|
- [ ] Plan đánh dấu `Status: Done`.
|
|
247
257
|
- [ ] Plan file đã `git mv` sang `plans/archive/<file>.md` (auto-archive).
|
|
248
258
|
- [ ] `plans/INDEX.md` đã chuyển plan từ Active sang Archived.
|
|
@@ -91,6 +91,7 @@ Không được tự tạo plan rồi tự approve.
|
|
|
91
91
|
- Định sửa file không nằm trong `## Files ảnh hưởng`.
|
|
92
92
|
- Định bỏ qua plan cho task chạm auth/payment/db/migration/deploy.
|
|
93
93
|
- Section `## Hiểu yêu cầu` trống mà vẫn định code.
|
|
94
|
+
- Định đổi/đảo kiến trúc đã có ADR mà plan thiếu section `## Quyết định kiến trúc` (supersede).
|
|
94
95
|
|
|
95
96
|
**Tất cả đều nghĩa là: DỪNG, quay lại review gate.**
|
|
96
97
|
|
|
@@ -118,6 +119,17 @@ HOẶC
|
|
|
118
119
|
|
|
119
120
|
<1-2 câu: làm gì, vì sao>
|
|
120
121
|
|
|
122
|
+
## Quyết định kiến trúc
|
|
123
|
+
|
|
124
|
+
> CHỈ BẮT BUỘC khi plan ĐỔI/ĐẢO kiến trúc một module (đổi cách triển khai, revert, chọn lại pattern). Bỏ section này cho plan thường. Hook `verify-completion` Check 11 chặn nếu section này khai báo đổi kiến trúc nhưng KHÔNG sinh/sửa ADR trong `docs/decisions/`.
|
|
125
|
+
|
|
126
|
+
- **ADR liên quan**: ADR-NNNN (đọc trước khi đổi).
|
|
127
|
+
- **Supersede**: `Supersede ADR-XXXX` (nếu đảo ngược quyết định cũ).
|
|
128
|
+
- **Lý do root cause cũ không còn đúng**: <chứng minh vì sao phương án cũ từng bị loại nay đã ổn — nếu không chứng minh được thì KHÔNG đổi>.
|
|
129
|
+
- **ADR sẽ tạo/cập nhật ở Bước 5**: `docs/decisions/NNNN-<slug>.md`.
|
|
130
|
+
|
|
131
|
+
Xem skill `decision-recorder` cho quy trình supersede đầy đủ.
|
|
132
|
+
|
|
121
133
|
## Modules ảnh hưởng
|
|
122
134
|
|
|
123
135
|
> Hook `verify-completion` Check 6 sẽ chặn nếu module ở đây không có README. Quy ước slug xem skill `module-architect` Bước 2.1.
|
|
@@ -38,6 +38,16 @@ description: "Dùng khi bắt đầu phân tích một yêu cầu trước khi t
|
|
|
38
38
|
- **KHÔNG scan** `docs/architecture/`, `docs/api/`, `docs/security/` trừ khi yêu cầu user trực tiếp đụng đến.
|
|
39
39
|
- Sau khi đọc README → mới được Read/Grep/Glob source code của module đó (hook đã unlock).
|
|
40
40
|
|
|
41
|
+
### Bước 1.3b — Đọc trí nhớ quyết định (ADR) — CHỐNG FLIP-FLOP
|
|
42
|
+
|
|
43
|
+
> **HOOK ENFORCEMENT**: nếu `docs/decisions/INDEX.md` tồn tại, hook `enforce-docs-first` chặn Read source cho tới khi đã đọc `docs/decisions/`. Marker: `<workspace>/.cursor/.decisions-read` (auto-clean mỗi sessionStart).
|
|
44
|
+
|
|
45
|
+
- Đọc `docs/decisions/INDEX.md` → tìm ADR liên quan tới module candidate (xem cột Module / link trong README module).
|
|
46
|
+
- **Nếu yêu cầu có dấu hiệu đổi/đảo kiến trúc** (đổi cách triển khai, "chuyển sang...", "thử cách khác", revert) → BẮT BUỘC đọc ADR `Accepted (LOCKED)` của module đó TRƯỚC.
|
|
47
|
+
- Kiểm tra phần **Rejected alternatives** + **Failed approaches log**: phương án bạn định chuyển sang có phải cái đã từng bị loại/hỏng không? Nếu CÓ và root cause còn đúng → KHÔNG lặp lại (đây chính là cái bẫy `A1 → B1 → A1`).
|
|
48
|
+
- Khi ADR tóm tắt chưa đủ ngữ cảnh → theo link **`Plan nguồn`** xuống `plans/archive/<file>.md` (tầng chi tiết, chỉ nạp khi cần).
|
|
49
|
+
- Muốn đổi kiến trúc đã khóa → áp dụng skill `decision-recorder` (quy trình supersede tường minh).
|
|
50
|
+
|
|
41
51
|
#### Anti-pattern token waste
|
|
42
52
|
|
|
43
53
|
- ❌ Đọc tất cả `docs/modules/*/README.md` "để hiểu toàn bộ dự án" → 19 module × 30 dòng = ~9k tokens phí phạm.
|
|
@@ -155,6 +165,7 @@ Subagent trả về tóm tắt + danh sách câu hỏi, parent agent dùng để
|
|
|
155
165
|
## Output bắt buộc trước khi sang Bước 2
|
|
156
166
|
|
|
157
167
|
- [ ] Đã đọc `docs/INDEX.md` (hook unlock cho phép Read source).
|
|
168
|
+
- [ ] Đã đọc `docs/decisions/INDEX.md` + ADR liên quan (nếu tồn tại); nếu định đổi kiến trúc → đã đọc ADR đang khóa + check root cause cũ.
|
|
158
169
|
- [ ] Tóm tắt yêu cầu (2-3 câu).
|
|
159
170
|
- [ ] Liệt kê module ảnh hưởng (slug + trạng thái có sẵn / NEW / backfill).
|
|
160
171
|
- [ ] Giả thuyết / edge cases / rủi ro.
|
|
@@ -80,6 +80,15 @@ Sau **3+ fix thất bại**, đây KHÔNG phải giả thuyết sai mà là **ki
|
|
|
80
80
|
|
|
81
81
|
→ **DỪNG, hỏi user** trước khi thử fix thứ 4: pattern này có sai từ gốc không? Có nên đổi kiến trúc thay vì vá tiếp không?
|
|
82
82
|
|
|
83
|
+
### Persist root cause vào ADR (CHỐNG FLIP-FLOP xuyên session)
|
|
84
|
+
|
|
85
|
+
Khi đã **xác nhận** một approach/kiến trúc hỏng do bản chất (không phải bug vặt):
|
|
86
|
+
|
|
87
|
+
1. Tìm ADR liên quan trong `docs/decisions/` (theo module). Nếu chưa có ADR cho concern đó → tạo qua skill `decision-recorder`.
|
|
88
|
+
2. **Append vào "Failed approaches log"** của ADR: `YYYY-MM-DD — <approach> — root cause: <nguyên nhân gốc>`. (Phần này miễn giới hạn dòng.)
|
|
89
|
+
3. Mục đích: session sau đọc ADR sẽ KHÔNG thử lại đúng approach đã hỏng (đây chính là cái bẫy `A1 → B1 → A1`).
|
|
90
|
+
4. Nếu quyết định **đổi kiến trúc** → theo quy trình supersede của `decision-recorder` (tạo ADR mới + đổi ADR cũ sang Superseded), KHÔNG revert lặng lẽ.
|
|
91
|
+
|
|
83
92
|
## Bảng Rationalization (cái cớ → sự thật)
|
|
84
93
|
|
|
85
94
|
| Cái cớ | Sự thật |
|
|
@@ -120,3 +129,4 @@ Nếu bắt gặp mình đang nghĩ:
|
|
|
120
129
|
|
|
121
130
|
- `test-strategy` — viết test reproduce bug ở Phase 4 (khi cần TDD-first).
|
|
122
131
|
- `plan-writer` — nếu fix biến thành refactor lớn/đổi kiến trúc → dừng, tạo plan.
|
|
132
|
+
- `decision-recorder` — persist root cause của approach hỏng vào ADR + quy trình supersede khi đổi kiến trúc (chống flip-flop).
|
package/templates/AGENTS.md.tpl
CHANGED
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
1. Read `docs/INDEX.md` (Modules + Token Routing) → match keyword user → 1-3 module candidate.
|
|
22
22
|
2. Read `plans/INDEX.md` (check overlap với plan active).
|
|
23
23
|
3. Read `docs/modules/<candidate>/README.md` (CHỈ module liên quan, không scan toàn bộ).
|
|
24
|
-
4. Read
|
|
25
|
-
5.
|
|
26
|
-
6.
|
|
24
|
+
4. **Nếu có `docs/decisions/INDEX.md`**: Read nó + ADR liên quan của module candidate (hook `enforce-docs-first` chặn Read source nếu chưa đọc `docs/decisions/`). Định đổi kiến trúc → đọc ADR đang khóa + check root cause cũ TRƯỚC (chống flip-flop A1→B1→A1).
|
|
25
|
+
5. Read source code module candidate (giờ hook unlock).
|
|
26
|
+
6. Tóm tắt yêu cầu + giả thuyết + edge case + rủi ro.
|
|
27
|
+
7. **Đặt câu hỏi critical để làm rõ yêu cầu** (`AskQuestion`) HOẶC khai báo `Lý do skip clarify` cho task trivial (typo, bump version, đổi 1 hằng số). Mục đích: làm rõ yêu cầu người dùng, phân tích tất cả trường hợp có thể xảy ra, phản biện và đề xuất phương án tối ưu hơn nếu có. Số lượng câu hỏi tùy thuộc vào độ phức tạp của task.
|
|
27
28
|
|
|
28
29
|
### Bước 2 — Thiết kế module
|
|
29
30
|
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
- Khi đã có plan `In Progress`, hook `check-plan-exists` enforce file scope theo `## Files ảnh hưởng` / `## Files thay đổi`.
|
|
44
45
|
- Khi plan còn `Draft`, hook chặn code edit để tránh Agent tự approve plan.
|
|
45
46
|
- Plan đụng >2 module → BẮT BUỘC thêm subsection "Lý do cross-module".
|
|
47
|
+
- **Plan đổi/đảo kiến trúc** → BẮT BUỘC thêm section "## Quyết định kiến trúc" (`Supersede ADR-NNNN` + lý do root cause cũ hết đúng). Hook `verify-completion` Check 11 chặn nếu khai báo đổi kiến trúc mà không sinh ADR.
|
|
46
48
|
- Update `plans/INDEX.md` (thêm vào Active).
|
|
47
49
|
|
|
48
50
|
### Bước 4 — Triển khai
|
|
@@ -58,6 +60,7 @@
|
|
|
58
60
|
|
|
59
61
|
- Sync `docs/modules/<slug>/README.md` (Public API + Dependencies) nếu API đổi.
|
|
60
62
|
- Module mới → đảm bảo đã có trong bảng "Modules hiện có" + "Token Routing" của `docs/INDEX.md`.
|
|
63
|
+
- **Nếu session ra/đổi quyết định kiến trúc** → tạo/supersede ADR trong `docs/decisions/` + link vào README module (section "## Quyết định kiến trúc (ADR)") + cập nhật `docs/decisions/INDEX.md` (skill `decision-recorder`).
|
|
61
64
|
- Plan → `Status: Done` + tick mọi checkbox.
|
|
62
65
|
- **AUTO-ARCHIVE**: `git mv plans/<file>.md plans/archive/<file>.md` + update `plans/INDEX.md`.
|
|
63
66
|
|
|
@@ -83,7 +86,7 @@
|
|
|
83
86
|
|
|
84
87
|
## Skills có sẵn
|
|
85
88
|
|
|
86
|
-
**Core (luôn relevant)**: `requirement-analysis`, `module-architect`, `plan-writer`.
|
|
89
|
+
**Core (luôn relevant)**: `requirement-analysis`, `module-architect`, `plan-writer`, `decision-recorder`.
|
|
87
90
|
|
|
88
91
|
**Core conditional**: `safe-push` (Bước 6 — chỉ khi user yêu cầu push).
|
|
89
92
|
|
|
@@ -95,9 +98,9 @@
|
|
|
95
98
|
|
|
96
99
|
| Hook | Khi nào | Hậu quả |
|
|
97
100
|
|---|---|---|
|
|
98
|
-
| `enforce-docs-first` | preToolUse Read/Glob/Grep | BLOCK source code đọc nếu chưa Read `docs/INDEX.md` |
|
|
101
|
+
| `enforce-docs-first` | preToolUse Read/Glob/Grep | BLOCK source code đọc nếu chưa Read `docs/INDEX.md`; nếu có `docs/decisions/INDEX.md` thì còn BLOCK đến khi đã đọc `docs/decisions/` (chống flip-flop kiến trúc) |
|
|
99
102
|
| `check-plan-exists` | preToolUse Write/StrReplace/EditNotebook/Delete | ALLOW task thường không plan; BLOCK file nhạy cảm không plan, plan Draft, hoặc file ngoài scope plan |
|
|
100
|
-
| `verify-completion` | stop (Cursor) / `session.idle` (OpenCode) | Cursor BLOCK kết thúc nếu Check 5/6/9 fail
|
|
103
|
+
| `verify-completion` | stop (Cursor) / `session.idle` (OpenCode) | Cursor BLOCK kết thúc nếu Check 5/6/9/10/11 fail (gồm ADR supersede integrity + plan đổi kiến trúc thiếu ADR); OpenCode re-prompt nhắc hoàn tất, loop guard ≤4 lần |
|
|
101
104
|
|
|
102
105
|
## Override cho dự án này
|
|
103
106
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# ADR-{{NNNN}}: {{Tiêu đề quyết định}}
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
HƯỚNG DẪN (xóa khi viết xong):
|
|
5
|
+
- Token-lean: viết gạch đầu dòng, SOFT TARGET ~40 dòng. Đây là GUIDELINE, không phải luật cứng.
|
|
6
|
+
- Quy tắc vàng: "viết đủ để session sau KHÔNG lặp lại sai lầm, không hơn".
|
|
7
|
+
- ĐƯỢC PHÉP vượt 40 dòng cho phần root cause chi tiết và "Failed approaches log" (miễn trừ).
|
|
8
|
+
- Cấu trúc 2 tầng: file này = TÓM TẮT (luôn nạp). Chi tiết đầy đủ nằm ở "Plan nguồn" (nạp khi cần).
|
|
9
|
+
- Đánh số {{NNNN}} = max ADR hiện có + 1 (4 chữ số: 0001, 0002, ...).
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
**Status**: Accepted (LOCKED) <!-- Accepted (LOCKED) | Proposed | Superseded by ADR-MMMM -->
|
|
13
|
+
**Date**: {{YYYY-MM-DD}}
|
|
14
|
+
**Module(s)**: {{slug-module-liên-quan}}
|
|
15
|
+
**Plan nguồn**: {{plans/archive/<file>.md hoặc "—" nếu quyết định inline}}
|
|
16
|
+
**Supersedes**: {{ADR-XXXX hoặc "—"}}
|
|
17
|
+
|
|
18
|
+
## Context — vấn đề cần quyết
|
|
19
|
+
|
|
20
|
+
- (vì sao phải ra quyết định này)
|
|
21
|
+
|
|
22
|
+
## Decision — chọn gì (kiến trúc hiện hành)
|
|
23
|
+
|
|
24
|
+
- (kiến trúc/hướng được chọn — đây là cái đang chạy)
|
|
25
|
+
|
|
26
|
+
## Rejected alternatives — phương án bị loại + VÌ SAO
|
|
27
|
+
|
|
28
|
+
- **{{Phương án A}}**: bị loại vì {{lý do / root cause nếu đã thử & hỏng}}.
|
|
29
|
+
- (giữ sắc thái root cause để session sau không thử lại cái đã hỏng)
|
|
30
|
+
|
|
31
|
+
## Consequences — hệ quả, known limitations, accepted trade-offs
|
|
32
|
+
|
|
33
|
+
- (những hạn chế đã biết — gồm các "bug nhỏ" chấp nhận được, đừng để session sau tưởng là lỗi cần đổi kiến trúc)
|
|
34
|
+
|
|
35
|
+
## Failed approaches log — append-only (miễn giới hạn dòng)
|
|
36
|
+
|
|
37
|
+
<!-- systematic-debugging append vào đây khi 1 approach được XÁC NHẬN hỏng. Format: YYYY-MM-DD — approach — root cause. -->
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!-- KIT_VERSION: v{{VERSION}} -->
|
|
2
|
+
# Decisions Index (ADR)
|
|
3
|
+
|
|
4
|
+
> **Trí nhớ quyết định kiến trúc của dự án.** AI ở Bước 1 PHẢI đọc file này trước khi sửa source (hook `enforce-docs-first` chặn nếu chưa đọc). Mỗi quyết định kiến trúc = 1 file `docs/decisions/NNNN-<slug>.md`.
|
|
5
|
+
>
|
|
6
|
+
> **Mục đích**: chống flip-flop kiến trúc xuyên session — Agent biết đã chọn gì, vì sao, đã loại phương án nào (kèm root cause), và hạn chế đã biết.
|
|
7
|
+
|
|
8
|
+
## Cách dùng (BẮT BUỘC)
|
|
9
|
+
|
|
10
|
+
1. **Trước khi đổi kiến trúc 1 module** → đọc ADR `Accepted (LOCKED)` của module đó. KHÔNG đảo ngược khi chưa supersede tường minh.
|
|
11
|
+
2. **Tạo ADR mới**: copy `~/.cursor/templates/adr.md.tpl` (hoặc `~/.config/opencode/templates/adr.md.tpl`) → `docs/decisions/NNNN-<slug>.md`, đánh số `NNNN` = max hiện có + 1.
|
|
12
|
+
3. **Supersede (đảo/đổi quyết định cũ)**: tạo ADR mới với `Supersedes: ADR-XXXX` + chứng minh root cause cũ hết đúng; đổi ADR cũ sang `Status: Superseded by ADR-NNNN`. Cập nhật bảng dưới.
|
|
13
|
+
4. Cập nhật bảng dưới + link ADR vào `docs/modules/<slug>/README.md` (section "## Quyết định kiến trúc (ADR)").
|
|
14
|
+
5. Chi tiết: skill `decision-recorder`.
|
|
15
|
+
|
|
16
|
+
## Quyết định hiện có
|
|
17
|
+
|
|
18
|
+
<!-- BEGIN PROJECT DECISIONS -->
|
|
19
|
+
| ADR | Tiêu đề (ngắn) | Status | Module(s) | Date |
|
|
20
|
+
|---|---|---|---|---|
|
|
21
|
+
| (chưa có) | | | | |
|
|
22
|
+
<!-- END PROJECT DECISIONS -->
|
|
23
|
+
|
|
24
|
+
## Quy ước
|
|
25
|
+
|
|
26
|
+
- File: `docs/decisions/NNNN-<slug-kebab-case>.md` (NNNN = 4 chữ số tăng dần).
|
|
27
|
+
- Status: `Accepted (LOCKED)` (đang hiệu lực, khóa) → `Superseded by ADR-MMMM` (đã thay thế).
|
|
28
|
+
- ADR token-lean (soft target ~40 dòng); chi tiết đầy đủ ở `Plan nguồn` (tầng chi tiết, nạp khi cần).
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
| Đường dẫn | Mục đích |
|
|
10
10
|
|---|---|
|
|
11
11
|
| `docs/modules/<name>/README.md` | Mục đích, public API, deps của từng module |
|
|
12
|
+
| `docs/decisions/INDEX.md` + `docs/decisions/NNNN-*.md` | Trí nhớ quyết định kiến trúc (ADR): chọn gì, vì sao, loại phương án nào, root cause |
|
|
12
13
|
<!-- END PROJECT DOC STRUCTURE -->
|
|
13
14
|
|
|
14
15
|
## Modules hiện có
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
| email, mail, notification, thông báo, push, SMS | (vd: `notifications`) | |
|
|
35
36
|
| upload, file, ảnh, image, storage, S3 | (vd: `storage`) | |
|
|
36
37
|
| search, tìm kiếm, filter, sort | (vd: `search`) | |
|
|
38
|
+
| quyết định, kiến trúc, ADR, decision, architecture, vì sao, why, revert, đảo ngược, supersede | `docs/decisions/` (xem `docs/decisions/INDEX.md`) | Đọc ADR liên quan TRƯỚC khi đổi kiến trúc |
|
|
37
39
|
| (thêm dòng cho mỗi domain concept của dự án) | | |
|
|
38
40
|
<!-- END PROJECT TOKEN ROUTING -->
|
|
39
41
|
|
|
@@ -46,6 +48,11 @@
|
|
|
46
48
|
2. Update bảng "Modules hiện có" ở trên.
|
|
47
49
|
3. **BẮT BUỘC**: thêm 3-5 keyword vào bảng "Token Routing" (cho cả VN + EN).
|
|
48
50
|
4. Tạo source folder tương ứng (`src/modules/<name>/` hoặc `apps/<name>/` hoặc `packages/<name>/`).
|
|
51
|
+
5. Nếu module có quyết định kiến trúc đáng nhớ → tạo ADR (`docs/decisions/`) + link vào README module (section "## Quyết định kiến trúc (ADR)"). Xem skill `decision-recorder`.
|
|
52
|
+
|
|
53
|
+
## Decisions (ADR) hiện có
|
|
54
|
+
|
|
55
|
+
Xem `docs/decisions/INDEX.md` — trí nhớ quyết định kiến trúc (chống flip-flop xuyên session). Đọc ADR liên quan TRƯỚC khi đổi kiến trúc một module.
|
|
49
56
|
|
|
50
57
|
## Plans hiện có
|
|
51
58
|
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
- Status trong file: `Draft` (chờ user review) → `In Progress` (đã approve) → `Done`.
|
|
32
32
|
- **Auto-archive**: ngay khi đổi Status → Done, chạy `git mv plans/<file>.md plans/archive/<file>.md` và update bảng Archived bên trên.
|
|
33
33
|
- Plan template: 5 sections (Hiểu yêu cầu, Mục tiêu, Modules ảnh hưởng, Files ảnh hưởng, Tasks). Plan minimal chỉ dùng khi vẫn muốn audit cho task nhỏ.
|
|
34
|
+
- **Plan đổi/đảo kiến trúc** PHẢI có section "## Quyết định kiến trúc" (`Supersede ADR-NNNN` + lý do root cause cũ hết đúng) và sinh/cập nhật ADR trong `docs/decisions/` ở Bước 5. Xem skill `decision-recorder`.
|
|
34
35
|
- Skill: `~/.cursor/skills/plan-writer/SKILL.md`.
|
|
35
36
|
|
|
36
37
|
## Ghi chú riêng của dự án
|