openmoneta-dev-kit 2.1.0 → 2.2.1

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 CHANGED
@@ -1 +1 @@
1
- 2.1.0
1
+ 2.2.1
@@ -59,7 +59,7 @@ SUMMARY='# OpenMoneta Dev Kit v1.7.0 — Quy trình 6 bước (Adaptive Planning
59
59
  - **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.
60
60
  - **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`.
61
61
  - **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.
62
- - **B5 Update doc + close** (hook `verify-completion` Check 6 enforce): sync module README (Public API + Dependencies). Plan → `Status: Done` + auto-archive `git mv plans/<f>.md plans/archive/`.
62
+ - **B5 Update doc + close** (hook `verify-completion` Check 6 enforce): chạm module nào → sync README module đó NGAY trong session (Trách nhiệm/Public API/Dependencies + ràng buộc; Check 6 nudge nếu README không đổi). Thay đổi chạm kiến trúc mà KHÔNG có plan → vẫn phải tạo ADR (`decision-recorder`); Check 12 nudge khi đổi đáng kể mà thiếu ADR. Plan → `Status: Done` + auto-archive `git mv plans/<f>.md plans/archive/`.
63
63
  - **B6 Pre-push Sync + Safe Push** (skill `safe-push`, CONDITIONAL): chỉ khi user yêu cầu push/commit+push. `git fetch` + rebase trước push, conflict code logic → hỏi user, verify lại, `git push` thường, retry ≤3, KHÔNG `--force` shared branch.
64
64
 
65
65
  ## Sub-agents (delegate khi cần)
@@ -113,12 +113,17 @@ if [[ -x "$LIST_MODULES_SCRIPT" || -f "$LIST_MODULES_SCRIPT" ]]; then
113
113
  AFFECTED=$(bash "$LIST_MODULES_SCRIPT" "$CHANGES_FILE" 2>/dev/null || true)
114
114
 
115
115
  if [[ -n "$AFFECTED" ]]; then
116
+ # Danh sách path đã đổi trong session (để check README freshness — Tuyến 1).
117
+ CHANGED_PATHS=$(jq -r '.changes[] | .path' "$CHANGES_FILE" 2>/dev/null || true)
116
118
  while IFS=$'\t' read -r slug docs_path source_path; do
117
119
  [[ -z "$slug" ]] && continue
118
120
  MODULE_README="$WORKSPACE/$docs_path/README.md"
119
121
 
120
122
  if [[ ! -f "$MODULE_README" ]]; then
121
123
  ISSUES+=("❌ Module '$slug' (source: $source_path) bị sửa nhưng chưa có '$docs_path/README.md'. Tạo README 3 sections (Trách nhiệm + Public API + Dependencies) theo skill module-architect Bước 2.2 + cập nhật bảng 'Modules hiện có' và 'Token Routing' trong docs/INDEX.md.")
124
+ elif [[ "$LOOP_COUNT" -eq 0 ]] && ! printf '%s\n' "$CHANGED_PATHS" | grep -qFx "$docs_path/README.md"; then
125
+ # README freshness (nudge mềm 1 lần): module source đổi nhưng README chưa sync trong session.
126
+ ISSUES+=("⚠️→ Module '$slug' bị sửa source nhưng '$docs_path/README.md' KHÔNG được cập nhật trong session. Nếu có đổi Public API / Trách nhiệm / Dependencies / ràng buộc → sync README NGAY (skill module-architect Bước 5). Nếu chỉ fix nội bộ không đổi hợp đồng → bỏ qua, kết thúc lại lần nữa (nhắc 1 lần).")
122
127
  fi
123
128
  done <<< "$AFFECTED"
124
129
 
@@ -159,16 +164,34 @@ if [[ -n "$CHANGED_DECISIONS" ]]; then
159
164
  fi
160
165
 
161
166
  # Check 11: plan trong session khai báo đổi/đảo kiến trúc nhưng KHÔNG sinh/sửa ADR
167
+ ARCH_DECLARED=0
162
168
  for plan in $ALL_RECENT_PLANS; do
163
169
  [[ -z "$plan" || ! -f "$plan" ]] && continue
164
170
  if grep -qE "^##[[:space:]]+Quyết định kiến trúc" "$plan" 2>/dev/null; then
165
171
  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.")
172
+ if [[ -n "$DECL" ]]; then
173
+ ARCH_DECLARED=1
174
+ if [[ -z "$CHANGED_DECISIONS" ]]; then
175
+ 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.")
176
+ fi
168
177
  fi
169
178
  fi
170
179
  done
171
180
 
181
+ # Check 12: thay đổi "đáng kể" KHÔNG qua plan kiến trúc + KHÔNG có ADR → nudge mềm 1 lần.
182
+ # Backstop cho ca sửa code chạm kiến trúc nhưng không tạo plan (Check 11 không bắt được).
183
+ # Ngưỡng CHẶT (≥4 module HOẶC ≥12 file code) để hiếm khi kêu, tránh false-positive.
184
+ if [[ "$LOOP_COUNT" -eq 0 && -f "$WORKSPACE/docs/decisions/INDEX.md" && -z "$CHANGED_DECISIONS" && "$ARCH_DECLARED" -eq 0 ]]; then
185
+ NUM_CODE_FILES=$(jq -r '.changes[] | .path' "$CHANGES_FILE" 2>/dev/null \
186
+ | grep -vE '^(docs/|plans/|README|\.gitignore|\.env\.example|AGENTS\.md|\.cursor/|CHANGELOG|VERSION$|package(-lock)?\.json|pnpm-lock|yarn\.lock)' \
187
+ | grep -cvE '^$' || true)
188
+ NUM_CODE_FILES=${NUM_CODE_FILES:-0}
189
+ NUM_MODULES=${NUM_MODULES:-0}
190
+ if [[ "$NUM_MODULES" -ge 4 || "$NUM_CODE_FILES" -ge 12 ]]; then
191
+ ISSUES+=("⚠️→ Session đụng $NUM_MODULES module / $NUM_CODE_FILES file code nhưng KHÔNG tạo/sửa ADR nào và KHÔNG có plan khai báo kiến trúc. NẾU đây là quyết định kiến trúc (đổi data flow, đổi cách module giao tiếp, thay/đảo 1 cơ chế, fix bug bằng cách đổi thiết kế) → tạo ADR (skill decision-recorder) hoặc nâng lên plan TRƯỚC khi đóng. Nếu chỉ là thay đổi thường (refactor nội bộ, đổi text, thêm field) → bỏ qua, kết thúc lại lần nữa (nhắc 1 lần).")
192
+ fi
193
+ fi
194
+
172
195
  # === Output ===
173
196
  WARN_STR=""
174
197
  if [[ ${#WARNINGS[@]} -gt 0 ]]; then
@@ -16,11 +16,11 @@
16
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
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
- - **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]`.
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]`. **Bug-fix hoá ra chạm kiến trúc** (đổi data flow / cách module giao tiếp / thay-đảo cơ chế) → nâng lên B3 hoặc tạo ADR, KHÔNG âm thầm sửa.
20
20
  - **B5 Update doc + close plan** — **mandatory closing checklist** (plugin guard `event:session.idle` sẽ re-prompt nếu thiếu):
21
- - [ ] Sync `docs/modules/<slug>/README.md` cho mọi module bị sửa: **Trách nhiệm + Public API + Dependencies**.
21
+ - [ ] Sync `docs/modules/<slug>/README.md` cho MỌI module bị sửa NGAY trong session: **Trách nhiệm + Public API + Dependencies** (+ **Lưu ý/Ràng buộc** nếu có invariant mới). Guard Check 6 nudge nếu module source đổi mà README không đổi.
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
+ - [ ] Nếu session đổi kiến trúc (kể cả KHÔNG có plan) → tạo/supersede ADR trong `docs/decisions/` + link vào README module + cập nhật `docs/decisions/INDEX.md` (skill `decision-recorder`). Guard Check 12 nudge khi đổi đáng kể (≥4 module / ≥12 file) mà thiếu ADR.
24
24
  - [ ] Plan → `Status: Done` + tick mọi checkbox.
25
25
  - [ ] **AUTO-ARCHIVE**: `git mv plans/<file>.md plans/archive/<file>.md` + update `plans/INDEX.md` (Active → Archived).
26
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.
@@ -32,7 +32,7 @@
32
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.
33
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`).
34
34
  - OpenMoneta subagents được cài vào `~/.config/opencode/agents/`.
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
+ - 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 tồn tại + freshness nudge (Check 6), close plan, supersede ADR (Check 10/11), ADR backstop khi đổi đáng kể không plan (Check 12), re-prompt nhắc hoàn tất với loop guard ≤4 lần).
36
36
 
37
37
  ## Khi Bắt Đầu Task
38
38
 
@@ -320,8 +320,10 @@ function planHasUnderstanding(planPath: string): boolean {
320
320
  return foundHeading && hasContent
321
321
  }
322
322
 
323
- // Build danh sách issue tương đương verify-completion Check 5/6/9.
324
- function buildVerifyIssues(root: string): string[] {
323
+ // Build danh sách issue tương đương verify-completion Check 5/6/9/10/11/12.
324
+ // firstNudge = true khi đây là lần re-prompt đầu (verify-loop count === 0) dùng cho
325
+ // các nudge mềm "1 lần" (README freshness + Check 12 ADR backstop), parity với LOOP_COUNT == 0.
326
+ function buildVerifyIssues(root: string, firstNudge = true): string[] {
325
327
  const paths = readSessionChanges(root)
326
328
  if (paths.length === 0) return []
327
329
  if (!paths.some((p) => !NON_CODE_CHANGE.test(p))) return []
@@ -366,11 +368,17 @@ function buildVerifyIssues(root: string): string[] {
366
368
  }
367
369
  }
368
370
 
369
- // Check 6: module bị sửa phải có docs/modules/<slug>/README.md
371
+ // Check 6: module bị sửa phải có docs/modules/<slug>/README.md (+ README freshness — Tuyến 1)
370
372
  for (const { slug, docsPath } of inferModules(paths)) {
371
- if (!fs.existsSync(path.join(root, docsPath, "README.md"))) {
373
+ const readmeRel = `${docsPath}/README.md`
374
+ if (!fs.existsSync(path.join(root, readmeRel))) {
372
375
  issues.push(
373
- `❌ Module '${slug}' bị sửa nhưng thiếu '${docsPath}/README.md'. Tạo README 3 sections (Trách nhiệm + Public API + Dependencies) + cập nhật docs/INDEX.md (Bước 2/5).`,
376
+ `❌ Module '${slug}' bị sửa nhưng thiếu '${readmeRel}'. Tạo README 3 sections (Trách nhiệm + Public API + Dependencies) + cập nhật docs/INDEX.md (Bước 2/5).`,
377
+ )
378
+ } else if (firstNudge && !paths.includes(readmeRel)) {
379
+ // README freshness (nudge mềm 1 lần): module source đổi nhưng README chưa sync.
380
+ issues.push(
381
+ `⚠️→ Module '${slug}' bị sửa source nhưng '${readmeRel}' KHÔNG được cập nhật trong session. Nếu có đổi Public API / Trách nhiệm / Dependencies / ràng buộc → sync README NGAY (Bước 5). Nếu chỉ fix nội bộ không đổi hợp đồng → bỏ qua, kết thúc lại lần nữa (nhắc 1 lần).`,
374
382
  )
375
383
  }
376
384
  }
@@ -403,6 +411,7 @@ function buildVerifyIssues(root: string): string[] {
403
411
  }
404
412
 
405
413
  // Check 11: plan khai báo "## Quyết định kiến trúc" (đổi/đảo) nhưng không sinh/sửa ADR
414
+ let archDeclared = false
406
415
  for (const plan of planSet) {
407
416
  if (!fs.existsSync(plan)) continue
408
417
  const lines = fs.readFileSync(plan, "utf8").split(/\r?\n/)
@@ -416,9 +425,32 @@ function buildVerifyIssues(root: string): string[] {
416
425
  if (inSection && /^##\s+/.test(line)) break
417
426
  if (inSection && /supersede|đổi kiến trúc|revert|đảo ngược/i.test(line)) declared = true
418
427
  }
419
- if (declared && changedDecisions.length === 0) {
428
+ if (declared) {
429
+ archDeclared = true
430
+ if (changedDecisions.length === 0) {
431
+ issues.push(
432
+ `❌ 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.`,
433
+ )
434
+ }
435
+ }
436
+ }
437
+
438
+ // Check 12: thay đổi "đáng kể" KHÔNG qua plan kiến trúc + KHÔNG có ADR → nudge mềm 1 lần.
439
+ // Backstop cho ca sửa code chạm kiến trúc nhưng không tạo plan. Ngưỡng CHẶT (≥4 module HOẶC
440
+ // ≥12 file code) để hiếm khi kêu, tránh false-positive.
441
+ if (
442
+ firstNudge &&
443
+ fs.existsSync(path.join(root, "docs", "decisions", "INDEX.md")) &&
444
+ changedDecisions.length === 0 &&
445
+ !archDeclared
446
+ ) {
447
+ const numModules = inferModules(paths).length
448
+ const numCodeFiles = paths.filter(
449
+ (p) => !/^(docs\/|plans\/|README|\.gitignore|\.env\.example|AGENTS\.md|\.cursor\/|CHANGELOG|VERSION$|package(-lock)?\.json|pnpm-lock|yarn\.lock)/.test(p),
450
+ ).length
451
+ if (numModules >= 4 || numCodeFiles >= 12) {
420
452
  issues.push(
421
- `❌ Plan '${path.basename(plan)}''## Quyết định kiến trúc' khai báo đổi/đảo kiến trúc nhưng KHÔNG docs/decisions/*.md được tạo/cập nhật. Tạo/supersede ADR (skill decision-recorder) trước khi kết thúc.`,
453
+ `⚠️→ Session đụng ${numModules} module / ${numCodeFiles} file code nhưng KHÔNG tạo/sửa ADR nào và KHÔNG plan khai báo kiến trúc. NẾU đây là quyết định kiến trúc (đổi data flow, đổi cách module giao tiếp, thay/đảo 1 chế, fix bug bằng cách đổi thiết kế) → tạo ADR (skill decision-recorder) hoặc nâng lên plan TRƯỚC khi đóng. Nếu chỉ là thay đổi thường → bỏ qua, kết thúc lại lần nữa (nhắc 1 lần).`,
422
454
  )
423
455
  }
424
456
  }
@@ -518,7 +550,7 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
518
550
  {
519
551
  loaded_at: new Date().toISOString(),
520
552
  root,
521
- version: "2.0.2",
553
+ version: "2.2.0",
522
554
  load_count: globalState[globalKey],
523
555
  },
524
556
  null,
@@ -686,8 +718,12 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
686
718
  }) => {
687
719
  if (event.type !== "session.idle") return
688
720
  try {
689
- const issues = buildVerifyIssues(root)
690
721
  const sessionID = asString(event.properties?.sessionID)
722
+ const loop = readVerifyLoop(root)
723
+ const count = loop.sessionID === sessionID ? loop.count : 0
724
+
725
+ // count === 0 ⇒ lần nhắc đầu ⇒ bật các nudge mềm "1 lần" (parity LOOP_COUNT == 0).
726
+ const issues = buildVerifyIssues(root, count === 0)
691
727
 
692
728
  if (issues.length === 0) {
693
729
  clearSessionChanges(root)
@@ -695,9 +731,6 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
695
731
  return
696
732
  }
697
733
 
698
- const loop = readVerifyLoop(root)
699
- const count = loop.sessionID === sessionID ? loop.count : 0
700
-
701
734
  if (count >= VERIFY_LOOP_LIMIT) {
702
735
  console.warn(
703
736
  `[OpenMoneta Dev Kit] verify-completion: đã nhắc ${count} lần, graceful exit. ` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmoneta-dev-kit",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
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",
@@ -19,8 +19,12 @@ KHÔNG ĐẢO NGƯỢC một kiến trúc đã Accepted (LOCKED) khi CHƯA:
19
19
 
20
20
  ## Khi nào TẠO ADR
21
21
 
22
+ > **Trigger này áp dụng kể cả KHÔNG có plan.** Nếu đang fix bug / sửa code mà chạm 1 trong các dấu hiệu dưới → DỪNG, tạo ADR NGAY (hoặc nâng lên plan), KHÔNG âm thầm sửa rồi đóng session. Hook Check 12 sẽ nudge nếu thay đổi đáng kể mà thiếu ADR.
23
+
22
24
  - 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).
25
+ - Đổi cách triển khai một tính năng đang chạy (vd: polling → websocket; DB heartbeat → in-memory).
26
+ - **Đổi data flow / đổi cách các module giao tiếp với nhau** (đổi contract giữa module, đổi nguồn sự thật).
27
+ - **Thay/đảo một cơ chế cốt lõi** dù khởi đầu chỉ là "fix bug" — fix bug bằng cách ĐỔI THIẾT KẾ = quyết định kiến trúc.
24
28
  - `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
29
  - Đảo ngược / thay thế một quyết định cũ.
26
30
 
@@ -70,7 +70,7 @@ Khi nào IGNORE rule (không tách dù dài):
70
70
  - Config files (webpack, playwright, biome, ...).
71
71
  - File có cohesion cực cao (vd 1 state machine 500 dòng cho 1 workflow rõ ràng).
72
72
 
73
- #### 3. Mỗi module có `README.md` riêng (3 sections, lean)
73
+ #### 3. Mỗi module có `README.md` riêng (3 section bắt buộc + 2 tùy, lean)
74
74
 
75
75
  ```markdown
76
76
  # <Tên module>
@@ -82,13 +82,27 @@ Khi nào IGNORE rule (không tách dù dài):
82
82
  - `funcA(arg1: X, arg2: Y): Z` — mô tả 1 dòng
83
83
  - `class Foo` — mô tả 1 dòng
84
84
 
85
+ ## Lưu ý / Ràng buộc (invariants) <!-- TÙY: chỉ khi có ràng buộc không hiển nhiên -->
86
+
87
+ - <quy tắc phải giữ / cạm bẫy, vd: "phải init DB trước khi gọi", "chạy cả Cursor + OpenCode nên giữ path trừu tượng">
88
+
89
+ ## Quyết định kiến trúc (ADR) <!-- TÙY: chỉ khi module có ADR -->
90
+
91
+ - [ADR-0003](../../decisions/0003-...md) — <1 dòng> (Accepted)
92
+
85
93
  ## Dependencies
86
94
 
87
95
  - Internal: `modules/auth`, `modules/db`
88
96
  - External: `axios`, `zod`
89
97
  ```
90
98
 
91
- > Chỉ 3 sections. KHÔNG thêm Owner/Stability/Cách dùng/Anti-patterns vào README mặc định — chúng dễ stale và AI hiếm khi dùng. Nếu module có quirk đặc biệt (vd phải init theo thứ tự), thêm section `## Lưu ý` ngắn ≤5 bullet.
99
+ **3 section BẮT BUỘC**: Trách nhiệm + Public API + Dependencies.
100
+
101
+ **2 section TÙY** (thêm khi có, vì là trí nhớ chống regression — đừng bỏ nếu thật sự có):
102
+ - **`## Lưu ý / Ràng buộc (invariants)`** (≤5 bullet): ràng buộc/cạm bẫy ngầm mà session sau dễ phá nếu không biết (thứ tự init, side-effect, môi trường chạy, giả định dữ liệu). Đây là tri thức ngầm hay mất giữa các session → gây regression; ghi ngắn gọn ngay khi phát hiện.
103
+ - **`## Quyết định kiến trúc (ADR)`**: list ADR-NNNN chi phối module (link). Tạo qua skill `decision-recorder`.
104
+
105
+ > KHÔNG thêm Owner/Stability/Cách dùng/tutorial — dễ stale, AI hiếm dùng. Giữ README lean: chỉ ghi cái session sau THỰC SỰ cần để không hiểu lệch / không phá đồ.
92
106
 
93
107
  ### Cấu trúc folder mẫu
94
108
 
@@ -204,7 +218,7 @@ Quy tắc maintenance:
204
218
 
205
219
  ### Bước 5 workflow — Sync module README (BẮT BUỘC)
206
220
 
207
- > Hook `verify-completion` Check 6 sẽ **CHẶN** session kết thúc nếu module bị sửa nhưng không `docs/modules/<slug>/README.md`.
221
+ > Hook `verify-completion` Check 6: **CHẶN** nếu module bị sửa nhưng thiếu `docs/modules/<slug>/README.md`; và **nudge mềm 1 lần** (README freshness) nếu module source đổi nhưng README KHÔNG được cập nhật trong session. Chạm module nào → sync README module đó NGAY trong session, KHÔNG để "sửa sau".
208
222
 
209
223
  **Cách xác định module từ file path** (cho file đã sửa trong session):
210
224
 
@@ -227,6 +241,7 @@ Quy tắc maintenance:
227
241
  4. Check section **Dependencies**:
228
242
  - Có `import` mới từ module khác? → thêm.
229
243
  - Có `import` bị xóa? → xóa.
244
+ 4b. Check section **Lưu ý / Ràng buộc**: thay đổi có tạo ra ràng buộc/cạm bẫy ngầm mới (thứ tự init, side-effect, giả định dữ liệu)? → thêm bullet ngắn. Đây là thứ chống regression cho session sau.
230
245
  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
246
  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.
232
247
 
@@ -47,6 +47,7 @@ description: "Dùng khi bắt đầu phân tích một yêu cầu trước khi t
47
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
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
49
  - Muốn đổi kiến trúc đã khóa → áp dụng skill `decision-recorder` (quy trình supersede tường minh).
50
+ - **Bug-fix hoá ra chạm kiến trúc**: nếu trong lúc làm 1 task tưởng nhỏ (fix bug) mà phát hiện phải đổi data flow / đổi cách module giao tiếp / thay-đảo cơ chế cốt lõi → KHÔNG âm thầm sửa rồi đóng. Nâng lên B3 (tạo plan) HOẶC tạo ADR ngay (skill `decision-recorder`), kể cả khi task khởi đầu không có plan.
50
51
 
51
52
  #### Anti-pattern token waste
52
53
 
@@ -1,7 +1,7 @@
1
1
  const { execSync } = require("node:child_process")
2
2
  const path = require("node:path")
3
3
  const fs = require("node:fs")
4
- const { getPkgRoot, isInstalled, isWindows, runScript, getProjectRegistry } = require("../lib/paths")
4
+ const { getPkgRoot, isInstalled, installedVersion, isWindows, runScript, getProjectRegistry } = require("../lib/paths")
5
5
  const { getLocalVersion, getLatestVersion } = require("../lib/version")
6
6
 
7
7
  function isOpenmonetaProject(dir) {
@@ -210,9 +210,16 @@ async function run(args) {
210
210
  process.exit(1)
211
211
  }
212
212
 
213
- if (local && latest && local === latest && autoYes && !force) {
214
- console.log(`\n Already at latest version (v${local}), skipping. Dùng --force để cài lại.`)
215
- return
213
+ // Early-skip chỉ áp dụng cho update global thuần (không yêu cầu sync project).
214
+ // Phải so version ĐÃ CÀI (installedVersion) với version sẽ cài (local) — KHÔNG chỉ
215
+ // so source-vs-npm — nếu không, khi source==npm mà global cũ hơn sẽ skip nhầm.
216
+ if (autoYes && !force && !allProjects && !scan) {
217
+ const targets = ["cursor", "opencode"].filter((t) => isInstalled(t))
218
+ const globalsCurrent = targets.length > 0 && targets.every((t) => installedVersion(t) === local)
219
+ if (local && latest && local === latest && globalsCurrent) {
220
+ console.log(`\n ✅ Already at latest version (v${local}) + global đã đồng bộ, skipping. Dùng --force để cài lại.`)
221
+ return
222
+ }
216
223
  }
217
224
 
218
225
  console.log(`\n 🔄 Updating OpenMoneta Dev Kit...`)
@@ -41,10 +41,12 @@
41
41
 
42
42
  ## Cách thêm module mới
43
43
 
44
- 1. Tạo `docs/modules/<module-name>/README.md` với 3 sections:
44
+ 1. Tạo `docs/modules/<module-name>/README.md` với 3 section BẮT BUỘC + 2 section TÙY:
45
45
  - **Trách nhiệm**: 1 câu không có "và".
46
46
  - **Public API**: function/class export.
47
47
  - **Dependencies**: internal + external.
48
+ - *(tùy)* **Lưu ý / Ràng buộc (invariants)**: ≤5 bullet ràng buộc/cạm bẫy ngầm (thứ tự init, side-effect, giả định dữ liệu) — thêm khi có, để session sau không phá đồ.
49
+ - *(tùy)* **Quyết định kiến trúc (ADR)**: link ADR-NNNN chi phối module (xem `docs/decisions/` + skill `decision-recorder`).
48
50
  2. Update bảng "Modules hiện có" ở trên.
49
51
  3. **BẮT BUỘC**: thêm 3-5 keyword vào bảng "Token Routing" (cho cả VN + EN).
50
52
  4. Tạo source folder tương ứng (`src/modules/<name>/` hoặc `apps/<name>/` hoặc `packages/<name>/`).