musubix 3.4.0 → 3.4.2
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/README.md +50 -315
- package/bin/musubix.js +1 -9
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -57
- package/.github/AGENTS.md +0 -949
- package/.github/prompts/sdd-change-apply.prompt.md +0 -283
- package/.github/prompts/sdd-change-archive.prompt.md +0 -241
- package/.github/prompts/sdd-change-init.prompt.md +0 -269
- package/.github/prompts/sdd-design.prompt.md +0 -250
- package/.github/prompts/sdd-implement.prompt.md +0 -387
- package/.github/prompts/sdd-requirements.prompt.md +0 -193
- package/.github/prompts/sdd-review.prompt.md +0 -155
- package/.github/prompts/sdd-security.prompt.md +0 -228
- package/.github/prompts/sdd-steering.prompt.md +0 -269
- package/.github/prompts/sdd-tasks.prompt.md +0 -255
- package/.github/prompts/sdd-test.prompt.md +0 -230
- package/.github/prompts/sdd-validate.prompt.md +0 -304
- package/.github/skills/musubix-adr-generation/SKILL.md +0 -209
- package/.github/skills/musubix-best-practices/SKILL.md +0 -315
- package/.github/skills/musubix-c4-design/SKILL.md +0 -162
- package/.github/skills/musubix-code-generation/SKILL.md +0 -237
- package/.github/skills/musubix-domain-inference/SKILL.md +0 -196
- package/.github/skills/musubix-ears-validation/SKILL.md +0 -161
- package/.github/skills/musubix-sdd-workflow/SKILL.md +0 -217
- package/.github/skills/musubix-technical-writing/SKILL.md +0 -444
- package/.github/skills/musubix-test-generation/SKILL.md +0 -212
- package/.github/skills/musubix-traceability/SKILL.md +0 -141
- package/AGENTS.md +0 -1134
- package/LICENSE +0 -21
- package/README.ja.md +0 -313
- package/bin/musubix-mcp.js +0 -15
- package/docs/API-REFERENCE.md +0 -1425
- package/docs/GITHUB-ACTIONS-NPM-SETUP.md +0 -132
- package/docs/INSTALL-GUIDE.ja.md +0 -459
- package/docs/INSTALL-GUIDE.md +0 -459
- package/docs/MIGRATION-v3.0.md +0 -324
- package/docs/MUSUBI-enhancement_roadmap_20260105.md +0 -651
- package/docs/MUSUBIX-v3.0-User-Guide.md +0 -1357
- package/docs/MUSUBIXv2.2.0-Manual-outline.md +0 -136
- package/docs/MUSUBIXv2.2.0-Manual.md +0 -3123
- package/docs/MUSUBIXv2.3.5-Refactering.md +0 -1310
- package/docs/MUSUBIv1.6.1-enhancement_roadmap_20260105.md +0 -291
- package/docs/MUSUBIv2.2.0-USERGUIDE.md +0 -2079
- package/docs/ROADMAP-v1.5.md +0 -116
- package/docs/SwarmCoding.md +0 -1284
- package/docs/Test-prompt.md +0 -105
- package/docs/USER-GUIDE-v1.8.0.md +0 -2371
- package/docs/USER-GUIDE.ja.md +0 -2147
- package/docs/USER-GUIDE.md +0 -3022
- package/docs/YATA-GLOBAL-GUIDE.ja.md +0 -750
- package/docs/YATA-GLOBAL-GUIDE.md +0 -595
- package/docs/YATA-LOCAL-GUIDE.ja.md +0 -989
- package/docs/YATA-LOCAL-GUIDE.md +0 -730
- package/docs/adr/0001-real-time-pattern-learning-architecture-for-v1-5-0.md +0 -75
- package/docs/adr/0002-pattern-sharing-protocol-for-cross-team-collaborat.md +0 -79
- package/docs/adr/0003-owl-2-rl-implementation-strategy-for-advanced-infe.md +0 -90
- package/docs/adr/ADR-v3.4.0-001-deep-research-architecture.md +0 -217
- package/docs/adr/ADR-v3.4.0-002-search-provider-selection.md +0 -308
- package/docs/adr/ADR-v3.4.0-003-lm-api-integration.md +0 -475
- package/docs/enterprise-knowledge-management.md +0 -1737
- package/docs/evolution-from-musubi-to-musubix.md +0 -2170
- package/docs/getting-started-with-sdd.md +0 -1602
- package/docs/moodle-refactering-codegraph-musubix.md +0 -391
- package/docs/moodle-refactering-codegraph.md +0 -278
- package/docs/overview/MUSUBIX-CodeGraph.md +0 -322
- package/docs/overview/MUSUBIX-Core.md +0 -671
- package/docs/overview/MUSUBIX-Decisions.md +0 -494
- package/docs/overview/MUSUBIX-FormalVerify.md +0 -566
- package/docs/overview/MUSUBIX-Knowledge.md +0 -1231
- package/docs/overview/MUSUBIX-Learning.md +0 -837
- package/docs/overview/MUSUBIX-MCP-Server.md +0 -535
- package/docs/overview/MUSUBIX-Overview.md +0 -264
- package/docs/overview/MUSUBIX-Phase1-Complete.md +0 -271
- package/docs/overview/MUSUBIX-Phase2-Complete.md +0 -310
- package/docs/overview/MUSUBIX-Policy.md +0 -477
- package/docs/overview/MUSUBIX-Roadmap-v2.md +0 -399
- package/docs/overview/MUSUBIX-Security-Plan.md +0 -939
- package/docs/overview/MUSUBIX-Security-v2.1.md +0 -668
- package/docs/overview/MUSUBIX-Security.md +0 -891
- package/docs/overview/MUSUBIX-YATA.md +0 -666
- package/docs/overview/MUSUBIX-v2.2.0-Advanced-Learning.md +0 -513
- package/docs/overview/Neuro-SymbolicAI.md +0 -159
- package/docs/packages/knowledge.md +0 -594
- package/docs/qiita-linux-kernel-knowledge-graph.md +0 -596
- package/scripts/generate-quality-gate-report.ts +0 -106
- package/scripts/postinstall.js +0 -94
- package/steering/.musubi-version +0 -1
- package/steering/product.ja.md +0 -572
- package/steering/project.yml +0 -66
- package/steering/rules/constitution.md +0 -491
- package/steering/structure.ja.md +0 -503
- package/steering/tech.ja.md +0 -208
|
@@ -1,1357 +0,0 @@
|
|
|
1
|
-
# MUSUBIX v3.0 Git-Native Knowledge System ユーザーガイド
|
|
2
|
-
|
|
3
|
-
> 完全ガイド: @musubix/knowledge, @musubix/policy, @musubix/decisions
|
|
4
|
-
|
|
5
|
-
| 項目 | 内容 |
|
|
6
|
-
|------|------|
|
|
7
|
-
| **バージョン** | 3.0.0 |
|
|
8
|
-
| **最終更新** | 2026-01-12 |
|
|
9
|
-
| **前提条件** | Node.js >= 20.0.0, TypeScript >= 5.0 |
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 目次
|
|
14
|
-
|
|
15
|
-
1. [はじめに](#はじめに)
|
|
16
|
-
2. [インストール](#インストール)
|
|
17
|
-
3. [@musubix/knowledge - 知識グラフストア](#musubixknowledge---知識グラフストア)
|
|
18
|
-
4. [@musubix/policy - ポリシーエンジン](#musubixpolicy---ポリシーエンジン)
|
|
19
|
-
5. [@musubix/decisions - ADRマネージャー](#musubixdecisions---adrマネージャー)
|
|
20
|
-
6. [統合ユースケース](#統合ユースケース)
|
|
21
|
-
7. [CLI統合](#cli統合)
|
|
22
|
-
8. [トラブルシューティング](#トラブルシューティング)
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## はじめに
|
|
27
|
-
|
|
28
|
-
MUSUBIX v3.0では、従来のYATA(Yet Another Typed Architecture)に代わり、**Git-Native Knowledge System**を導入しました。この新システムは以下の3つのパッケージで構成されています:
|
|
29
|
-
|
|
30
|
-
| パッケージ | 役割 | ストレージ |
|
|
31
|
-
|-----------|------|-----------|
|
|
32
|
-
| `@musubix/knowledge` | 知識グラフ(エンティティ・リレーション) | `.knowledge/graph.json` |
|
|
33
|
-
| `@musubix/policy` | 9憲法条項の自動検証 | メモリ(設定ファイル任意) |
|
|
34
|
-
| `@musubix/decisions` | Architecture Decision Records | `docs/decisions/*.md` |
|
|
35
|
-
|
|
36
|
-
### 主な特徴
|
|
37
|
-
|
|
38
|
-
- **サーバーレス**: 外部データベース不要
|
|
39
|
-
- **Git-friendly**: JSON/Markdown形式で差分管理が容易
|
|
40
|
-
- **軽量**: 外部依存ゼロ
|
|
41
|
-
- **型安全**: TypeScriptで完全な型定義
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## インストール
|
|
46
|
-
|
|
47
|
-
### 全パッケージ一括インストール(推奨)
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm install musubix
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
これで `@musubix/knowledge`, `@musubix/policy`, `@musubix/decisions` がすべてインストールされます。
|
|
54
|
-
|
|
55
|
-
### 個別インストール
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# 知識グラフのみ
|
|
59
|
-
npm install @musubix/knowledge
|
|
60
|
-
|
|
61
|
-
# ポリシーエンジンのみ
|
|
62
|
-
npm install @musubix/policy
|
|
63
|
-
|
|
64
|
-
# ADRマネージャーのみ
|
|
65
|
-
npm install @musubix/decisions
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### インポート方法
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
// musubixパッケージから(推奨)
|
|
72
|
-
import { knowledge, policy, decisions } from 'musubix';
|
|
73
|
-
|
|
74
|
-
const store = knowledge.createKnowledgeStore('.knowledge');
|
|
75
|
-
const engine = policy.createPolicyEngine();
|
|
76
|
-
const manager = decisions.createDecisionManager('docs/decisions');
|
|
77
|
-
|
|
78
|
-
// 個別パッケージから
|
|
79
|
-
import { createKnowledgeStore } from '@musubix/knowledge';
|
|
80
|
-
import { createPolicyEngine } from '@musubix/policy';
|
|
81
|
-
import { createDecisionManager } from '@musubix/decisions';
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## @musubix/knowledge - 知識グラフストア
|
|
87
|
-
|
|
88
|
-
### 概要
|
|
89
|
-
|
|
90
|
-
要件(REQ)、設計(DES)、タスク(TSK)、コードなどのエンティティとその関係を管理する軽量な知識グラフです。
|
|
91
|
-
|
|
92
|
-
### ストレージ構造
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
.knowledge/
|
|
96
|
-
└── graph.json # 全エンティティとリレーション
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### クイックスタート
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { createKnowledgeStore } from '@musubix/knowledge';
|
|
103
|
-
|
|
104
|
-
const store = createKnowledgeStore('.knowledge');
|
|
105
|
-
|
|
106
|
-
// エンティティを追加
|
|
107
|
-
await store.putEntity({
|
|
108
|
-
id: 'REQ-001',
|
|
109
|
-
type: 'requirement',
|
|
110
|
-
name: 'ユーザー認証',
|
|
111
|
-
description: 'ユーザーがログインできること',
|
|
112
|
-
properties: { priority: 'P0', ears: 'THE system SHALL authenticate users' },
|
|
113
|
-
tags: ['security', 'auth'],
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// 保存(自動的に .knowledge/graph.json に書き込み)
|
|
117
|
-
await store.save();
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### API リファレンス
|
|
121
|
-
|
|
122
|
-
#### `createKnowledgeStore(basePath: string): KnowledgeStore`
|
|
123
|
-
|
|
124
|
-
Knowledge Store を作成します。
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
const store = createKnowledgeStore('.knowledge');
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
#### `putEntity(entity: Entity): Promise<void>`
|
|
131
|
-
|
|
132
|
-
エンティティを作成または更新します。
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
interface Entity {
|
|
136
|
-
id: string; // 一意のID(例: 'REQ-001', 'DES-001')
|
|
137
|
-
type: string; // タイプ(例: 'requirement', 'design', 'task', 'code')
|
|
138
|
-
name: string; // 表示名
|
|
139
|
-
description?: string; // 説明
|
|
140
|
-
properties?: Record<string, unknown>; // カスタムプロパティ
|
|
141
|
-
tags?: string[]; // タグ
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
await store.putEntity({
|
|
145
|
-
id: 'DES-001',
|
|
146
|
-
type: 'design',
|
|
147
|
-
name: 'JWT認証設計',
|
|
148
|
-
description: 'JWTトークンを使用した認証フロー',
|
|
149
|
-
properties: {
|
|
150
|
-
pattern: 'Token-Based Authentication',
|
|
151
|
-
components: ['AuthService', 'TokenValidator'],
|
|
152
|
-
},
|
|
153
|
-
tags: ['security', 'jwt'],
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
#### `getEntity(id: string): Promise<Entity | undefined>`
|
|
158
|
-
|
|
159
|
-
IDでエンティティを取得します。
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
const req = await store.getEntity('REQ-001');
|
|
163
|
-
if (req) {
|
|
164
|
-
console.log(req.name); // => 'ユーザー認証'
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
#### `deleteEntity(id: string): Promise<boolean>`
|
|
169
|
-
|
|
170
|
-
エンティティを削除します。
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
const deleted = await store.deleteEntity('REQ-999');
|
|
174
|
-
console.log(deleted); // => false(存在しない場合)
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
#### `addRelation(relation: Relation): Promise<void>`
|
|
178
|
-
|
|
179
|
-
エンティティ間のリレーションを追加します。
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
interface Relation {
|
|
183
|
-
id?: string; // リレーションID(省略可能)
|
|
184
|
-
source: string; // ソースエンティティID
|
|
185
|
-
target: string; // ターゲットエンティティID
|
|
186
|
-
type: string; // リレーションタイプ
|
|
187
|
-
properties?: Record<string, unknown>; // カスタムプロパティ
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// 設計が要件を実装
|
|
191
|
-
await store.addRelation({
|
|
192
|
-
source: 'DES-001',
|
|
193
|
-
target: 'REQ-001',
|
|
194
|
-
type: 'implements',
|
|
195
|
-
properties: { confidence: 0.95 },
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// タスクが設計を実現
|
|
199
|
-
await store.addRelation({
|
|
200
|
-
source: 'TSK-001',
|
|
201
|
-
target: 'DES-001',
|
|
202
|
-
type: 'realizes',
|
|
203
|
-
});
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
#### `getRelations(entityId: string): Promise<Relation[]>`
|
|
207
|
-
|
|
208
|
-
エンティティに関連するすべてのリレーションを取得します。
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
const relations = await store.getRelations('DES-001');
|
|
212
|
-
for (const rel of relations) {
|
|
213
|
-
console.log(`${rel.source} --${rel.type}--> ${rel.target}`);
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
#### `query(filter: QueryFilter): Promise<Entity[]>`
|
|
218
|
-
|
|
219
|
-
フィルタ条件でエンティティを検索します。
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
interface QueryFilter {
|
|
223
|
-
type?: string; // タイプでフィルタ
|
|
224
|
-
tags?: string[]; // タグでフィルタ(AND条件)
|
|
225
|
-
text?: string; // 名前・説明のテキスト検索
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// タイプで検索
|
|
229
|
-
const requirements = await store.query({ type: 'requirement' });
|
|
230
|
-
|
|
231
|
-
// タグで検索
|
|
232
|
-
const securityItems = await store.query({ tags: ['security'] });
|
|
233
|
-
|
|
234
|
-
// 複合条件
|
|
235
|
-
const securityReqs = await store.query({
|
|
236
|
-
type: 'requirement',
|
|
237
|
-
tags: ['security'],
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// テキスト検索
|
|
241
|
-
const authRelated = await store.query({ text: '認証' });
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
#### `traverse(startId: string, options?: TraverseOptions): Promise<Entity[]>`
|
|
245
|
-
|
|
246
|
-
グラフを走査して関連エンティティを取得します。
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
interface TraverseOptions {
|
|
250
|
-
direction?: 'outgoing' | 'incoming' | 'both'; // 走査方向
|
|
251
|
-
relationTypes?: string[]; // リレーションタイプでフィルタ
|
|
252
|
-
maxDepth?: number; // 最大深度(デフォルト: 3)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// REQ-001から出ていくリレーションを辿る
|
|
256
|
-
const downstream = await store.traverse('REQ-001', {
|
|
257
|
-
direction: 'incoming', // REQ-001をtargetとするリレーション
|
|
258
|
-
maxDepth: 2,
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// implementsリレーションのみ
|
|
262
|
-
const implementations = await store.traverse('REQ-001', {
|
|
263
|
-
direction: 'incoming',
|
|
264
|
-
relationTypes: ['implements'],
|
|
265
|
-
});
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
#### `save(): Promise<void>`
|
|
269
|
-
|
|
270
|
-
変更を永続化します。
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
await store.save();
|
|
274
|
-
// .knowledge/graph.json に保存
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
#### `load(): Promise<void>`
|
|
278
|
-
|
|
279
|
-
ディスクから読み込みます(通常は自動で呼ばれます)。
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
await store.load();
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### トレーサビリティの構築
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
// 完全なトレーサビリティチェーン
|
|
289
|
-
// REQ → DES → TSK → Code
|
|
290
|
-
|
|
291
|
-
// 1. 要件
|
|
292
|
-
await store.putEntity({
|
|
293
|
-
id: 'REQ-AUTH-001',
|
|
294
|
-
type: 'requirement',
|
|
295
|
-
name: 'ユーザー認証',
|
|
296
|
-
properties: { ears: 'WHEN user submits credentials, THE system SHALL verify and authenticate' },
|
|
297
|
-
tags: ['security'],
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// 2. 設計
|
|
301
|
-
await store.putEntity({
|
|
302
|
-
id: 'DES-AUTH-001',
|
|
303
|
-
type: 'design',
|
|
304
|
-
name: 'JWT認証フロー',
|
|
305
|
-
properties: { pattern: 'Token-Based Auth' },
|
|
306
|
-
tags: ['security'],
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
// 3. タスク
|
|
310
|
-
await store.putEntity({
|
|
311
|
-
id: 'TSK-AUTH-001',
|
|
312
|
-
type: 'task',
|
|
313
|
-
name: 'AuthService実装',
|
|
314
|
-
properties: { estimate: '4h' },
|
|
315
|
-
tags: ['implementation'],
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// 4. コード
|
|
319
|
-
await store.putEntity({
|
|
320
|
-
id: 'CODE-AUTH-001',
|
|
321
|
-
type: 'code',
|
|
322
|
-
name: 'AuthService.ts',
|
|
323
|
-
properties: { path: 'src/services/AuthService.ts' },
|
|
324
|
-
tags: ['service'],
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
// リレーション構築
|
|
328
|
-
await store.addRelation({ source: 'DES-AUTH-001', target: 'REQ-AUTH-001', type: 'implements' });
|
|
329
|
-
await store.addRelation({ source: 'TSK-AUTH-001', target: 'DES-AUTH-001', type: 'realizes' });
|
|
330
|
-
await store.addRelation({ source: 'CODE-AUTH-001', target: 'TSK-AUTH-001', type: 'fulfills' });
|
|
331
|
-
|
|
332
|
-
await store.save();
|
|
333
|
-
|
|
334
|
-
// トレーサビリティ確認
|
|
335
|
-
const chain = await store.traverse('REQ-AUTH-001', {
|
|
336
|
-
direction: 'incoming',
|
|
337
|
-
maxDepth: 10,
|
|
338
|
-
});
|
|
339
|
-
console.log('トレーサビリティチェーン:', chain.map(e => e.id));
|
|
340
|
-
// => ['REQ-AUTH-001', 'DES-AUTH-001', 'TSK-AUTH-001', 'CODE-AUTH-001']
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
---
|
|
344
|
-
|
|
345
|
-
## @musubix/policy - ポリシーエンジン
|
|
346
|
-
|
|
347
|
-
### 概要
|
|
348
|
-
|
|
349
|
-
MUSUBIX 9憲法条項を自動検証するポリシーエンジンです。プロジェクトが憲法に準拠しているかをチェックします。
|
|
350
|
-
|
|
351
|
-
### 9憲法条項
|
|
352
|
-
|
|
353
|
-
| ID | 条項 | 検証内容 | 重要度 |
|
|
354
|
-
|----|------|----------|--------|
|
|
355
|
-
| CONST-001 | Library-First | `packages/` ディレクトリの存在 | error |
|
|
356
|
-
| CONST-002 | CLI Interface | `bin/` または package.json の bin フィールド | error |
|
|
357
|
-
| CONST-003 | Test-First | テストファイルの存在 | error |
|
|
358
|
-
| CONST-004 | EARS Format | 要件ファイルのEARS形式 | error |
|
|
359
|
-
| CONST-005 | Traceability | `storage/traceability/` の存在 | error |
|
|
360
|
-
| CONST-006 | Project Memory | `steering/` ディレクトリの存在 | warning |
|
|
361
|
-
| CONST-007 | Design Patterns | `storage/design/` の存在 | warning |
|
|
362
|
-
| CONST-008 | Decision Records | `docs/decisions/` の存在 | warning |
|
|
363
|
-
| CONST-009 | Quality Gates | CI設定またはテスト設定の存在 | error |
|
|
364
|
-
|
|
365
|
-
### クイックスタート
|
|
366
|
-
|
|
367
|
-
```typescript
|
|
368
|
-
import { createPolicyEngine } from '@musubix/policy';
|
|
369
|
-
|
|
370
|
-
const engine = createPolicyEngine();
|
|
371
|
-
|
|
372
|
-
// プロジェクト全体を検証
|
|
373
|
-
const report = await engine.validateProject('/path/to/project');
|
|
374
|
-
|
|
375
|
-
console.log('合格:', report.passed);
|
|
376
|
-
console.log('違反:', report.violations.length);
|
|
377
|
-
|
|
378
|
-
for (const v of report.violations) {
|
|
379
|
-
console.log(`[${v.severity}] ${v.policyId}: ${v.message}`);
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### API リファレンス
|
|
384
|
-
|
|
385
|
-
#### `createPolicyEngine(options?: PolicyEngineOptions): IPolicyEngine`
|
|
386
|
-
|
|
387
|
-
ポリシーエンジンを作成します。
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
interface PolicyEngineOptions {
|
|
391
|
-
config?: PolicyConfig;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
interface PolicyConfig {
|
|
395
|
-
enabled?: string[]; // 有効にするポリシーID
|
|
396
|
-
disabled?: string[]; // 無効にするポリシーID
|
|
397
|
-
severity?: Record<string, Severity>; // 重要度のオーバーライド
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// デフォルト(全憲法条項が有効)
|
|
401
|
-
const engine = createPolicyEngine();
|
|
402
|
-
|
|
403
|
-
// 特定のポリシーを無効化
|
|
404
|
-
const engine = createPolicyEngine({
|
|
405
|
-
config: {
|
|
406
|
-
disabled: ['CONST-006', 'CONST-007'],
|
|
407
|
-
},
|
|
408
|
-
});
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
#### `listPolicies(category?: PolicyCategory): Policy[]`
|
|
412
|
-
|
|
413
|
-
登録済みポリシーの一覧を取得します。
|
|
414
|
-
|
|
415
|
-
```typescript
|
|
416
|
-
type PolicyCategory = 'constitution' | 'naming' | 'security' | 'quality' | 'custom';
|
|
417
|
-
|
|
418
|
-
// 全ポリシー
|
|
419
|
-
const all = engine.listPolicies();
|
|
420
|
-
|
|
421
|
-
// 憲法条項のみ
|
|
422
|
-
const constitution = engine.listPolicies('constitution');
|
|
423
|
-
|
|
424
|
-
for (const p of constitution) {
|
|
425
|
-
console.log(`${p.id}: ${p.name} - ${p.description}`);
|
|
426
|
-
}
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
#### `getPolicy(id: string): Policy | undefined`
|
|
430
|
-
|
|
431
|
-
IDでポリシーを取得します。
|
|
432
|
-
|
|
433
|
-
```typescript
|
|
434
|
-
const policy = engine.getPolicy('CONST-001');
|
|
435
|
-
if (policy) {
|
|
436
|
-
console.log(policy.description);
|
|
437
|
-
// => "Article I: Features must start as independent libraries"
|
|
438
|
-
}
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
#### `validate(context: ValidationContext, policyIds?: string[]): Promise<ValidationReport>`
|
|
442
|
-
|
|
443
|
-
コンテキストを検証します。
|
|
444
|
-
|
|
445
|
-
```typescript
|
|
446
|
-
interface ValidationContext {
|
|
447
|
-
filePath?: string; // ファイルパス
|
|
448
|
-
content?: string; // ファイル内容
|
|
449
|
-
projectPath?: string; // プロジェクトパス
|
|
450
|
-
config?: PolicyConfig;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
interface ValidationReport {
|
|
454
|
-
passed: boolean;
|
|
455
|
-
totalPolicies: number;
|
|
456
|
-
passedPolicies: number;
|
|
457
|
-
failedPolicies: number;
|
|
458
|
-
violations: Violation[];
|
|
459
|
-
timestamp: string;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// 特定のポリシーのみ検証
|
|
463
|
-
const report = await engine.validate(
|
|
464
|
-
{ projectPath: '/path/to/project' },
|
|
465
|
-
['CONST-001', 'CONST-003']
|
|
466
|
-
);
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
#### `validateFile(filePath: string, policyIds?: string[]): Promise<ValidationReport>`
|
|
470
|
-
|
|
471
|
-
単一ファイルを検証します。
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
const report = await engine.validateFile('storage/specs/REQ-001.md');
|
|
475
|
-
if (!report.passed) {
|
|
476
|
-
console.log('EARS形式に準拠していません');
|
|
477
|
-
}
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
#### `validateProject(projectPath: string, policyIds?: string[]): Promise<ValidationReport>`
|
|
481
|
-
|
|
482
|
-
プロジェクト全体を検証します。
|
|
483
|
-
|
|
484
|
-
```typescript
|
|
485
|
-
const report = await engine.validateProject('.');
|
|
486
|
-
|
|
487
|
-
if (report.passed) {
|
|
488
|
-
console.log('✅ すべての憲法条項に準拠しています');
|
|
489
|
-
} else {
|
|
490
|
-
console.log(`❌ ${report.failedPolicies} 件の違反があります:`);
|
|
491
|
-
|
|
492
|
-
for (const v of report.violations) {
|
|
493
|
-
const icon = v.severity === 'error' ? '🚫' : '⚠️';
|
|
494
|
-
console.log(`${icon} [${v.policyId}] ${v.message}`);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
#### `registerPolicy(policy: Policy): void`
|
|
500
|
-
|
|
501
|
-
カスタムポリシーを登録します。
|
|
502
|
-
|
|
503
|
-
```typescript
|
|
504
|
-
interface Policy {
|
|
505
|
-
id: string;
|
|
506
|
-
name: string;
|
|
507
|
-
description: string;
|
|
508
|
-
severity: 'error' | 'warning' | 'info';
|
|
509
|
-
category: PolicyCategory;
|
|
510
|
-
validate(context: ValidationContext): Promise<PolicyResult>;
|
|
511
|
-
fix?(context: ValidationContext): Promise<FixResult>; // オプション
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
engine.registerPolicy({
|
|
515
|
-
id: 'CUSTOM-001',
|
|
516
|
-
name: 'No Console Logs',
|
|
517
|
-
description: 'Production code must not contain console.log',
|
|
518
|
-
severity: 'warning',
|
|
519
|
-
category: 'quality',
|
|
520
|
-
async validate(ctx) {
|
|
521
|
-
if (ctx.content?.includes('console.log')) {
|
|
522
|
-
return {
|
|
523
|
-
passed: false,
|
|
524
|
-
violations: [{
|
|
525
|
-
policyId: 'CUSTOM-001',
|
|
526
|
-
message: 'console.log found in production code',
|
|
527
|
-
severity: 'warning',
|
|
528
|
-
location: { file: ctx.filePath },
|
|
529
|
-
}],
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
return { passed: true, violations: [] };
|
|
533
|
-
},
|
|
534
|
-
});
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
#### `loadPolicies(dir: string): Promise<void>`
|
|
538
|
-
|
|
539
|
-
ディレクトリからカスタムポリシーを読み込みます。
|
|
540
|
-
|
|
541
|
-
```typescript
|
|
542
|
-
// policies/my-policy.ts をエクスポートするとロードされる
|
|
543
|
-
await engine.loadPolicies('./policies');
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
### 検証レポートの活用
|
|
547
|
-
|
|
548
|
-
```typescript
|
|
549
|
-
const report = await engine.validateProject('.');
|
|
550
|
-
|
|
551
|
-
// サマリー出力
|
|
552
|
-
console.log(`
|
|
553
|
-
=== Policy Validation Report ===
|
|
554
|
-
Timestamp: ${report.timestamp}
|
|
555
|
-
Status: ${report.passed ? '✅ PASSED' : '❌ FAILED'}
|
|
556
|
-
Policies: ${report.passedPolicies}/${report.totalPolicies} passed
|
|
557
|
-
`);
|
|
558
|
-
|
|
559
|
-
// 違反の詳細
|
|
560
|
-
if (report.violations.length > 0) {
|
|
561
|
-
console.log('Violations:');
|
|
562
|
-
|
|
563
|
-
const byCategory = new Map<string, Violation[]>();
|
|
564
|
-
for (const v of report.violations) {
|
|
565
|
-
const policy = engine.getPolicy(v.policyId);
|
|
566
|
-
const cat = policy?.category ?? 'unknown';
|
|
567
|
-
if (!byCategory.has(cat)) byCategory.set(cat, []);
|
|
568
|
-
byCategory.get(cat)!.push(v);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
for (const [category, violations] of byCategory) {
|
|
572
|
-
console.log(`\n[${category}]`);
|
|
573
|
-
for (const v of violations) {
|
|
574
|
-
console.log(` - ${v.policyId}: ${v.message}`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
|
|
582
|
-
## @musubix/decisions - ADRマネージャー
|
|
583
|
-
|
|
584
|
-
### 概要
|
|
585
|
-
|
|
586
|
-
Architecture Decision Records(ADR)を管理するパッケージです。プロジェクトのアーキテクチャ決定を記録・追跡します。
|
|
587
|
-
|
|
588
|
-
### ストレージ構造
|
|
589
|
-
|
|
590
|
-
```
|
|
591
|
-
docs/decisions/
|
|
592
|
-
├── index.md # ADRインデックス(自動生成)
|
|
593
|
-
├── 0001-use-jwt-auth.md # ADR #1
|
|
594
|
-
├── 0002-adopt-ddd.md # ADR #2
|
|
595
|
-
└── ...
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
### ADRステータス
|
|
599
|
-
|
|
600
|
-
| ステータス | 説明 |
|
|
601
|
-
|-----------|------|
|
|
602
|
-
| `proposed` | 提案中(レビュー待ち) |
|
|
603
|
-
| `accepted` | 承認済み(有効) |
|
|
604
|
-
| `deprecated` | 非推奨(使用しないことを推奨) |
|
|
605
|
-
| `superseded` | 置き換え済み(別のADRに置き換わった) |
|
|
606
|
-
|
|
607
|
-
### クイックスタート
|
|
608
|
-
|
|
609
|
-
```typescript
|
|
610
|
-
import { createDecisionManager } from '@musubix/decisions';
|
|
611
|
-
|
|
612
|
-
const manager = createDecisionManager('docs/decisions');
|
|
613
|
-
|
|
614
|
-
// 新しいADRを作成
|
|
615
|
-
const adr = await manager.create({
|
|
616
|
-
title: 'JWT認証の採用',
|
|
617
|
-
context: 'ユーザー認証の仕組みが必要。セッション管理のオーバーヘッドを避けたい。',
|
|
618
|
-
decision: 'JWTトークンベースの認証を採用する。',
|
|
619
|
-
rationale: 'ステートレスでスケーラブル。マイクロサービス間でも利用可能。',
|
|
620
|
-
alternatives: ['セッションベース認証', 'OAuth2のみ'],
|
|
621
|
-
consequences: ['トークン失効の仕組みが必要', 'トークンサイズに注意'],
|
|
622
|
-
relatedRequirements: ['REQ-AUTH-001'],
|
|
623
|
-
decider: 'John Doe',
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
console.log(`ADR-${adr.id} created: ${adr.title}`);
|
|
627
|
-
// => ADR-0001 created: JWT認証の採用
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
### API リファレンス
|
|
631
|
-
|
|
632
|
-
#### `createDecisionManager(basePath: string): IDecisionManager`
|
|
633
|
-
|
|
634
|
-
Decision Manager を作成します。
|
|
635
|
-
|
|
636
|
-
```typescript
|
|
637
|
-
const manager = createDecisionManager('docs/decisions');
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
#### `create(draft: ADRDraft): Promise<ADR>`
|
|
641
|
-
|
|
642
|
-
新しいADRを作成します。
|
|
643
|
-
|
|
644
|
-
```typescript
|
|
645
|
-
interface ADRDraft {
|
|
646
|
-
title: string; // タイトル
|
|
647
|
-
context: string; // コンテキスト・背景
|
|
648
|
-
decision: string; // 決定内容
|
|
649
|
-
rationale?: string; // 理由
|
|
650
|
-
alternatives?: string[]; // 検討した代替案
|
|
651
|
-
consequences?: string[]; // 影響・結果
|
|
652
|
-
relatedRequirements?: string[]; // 関連する要件ID
|
|
653
|
-
decider?: string; // 決定者
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const adr = await manager.create({
|
|
657
|
-
title: 'DDDの採用',
|
|
658
|
-
context: 'ドメインロジックが複雑化している。',
|
|
659
|
-
decision: 'Domain-Driven Designを採用し、ドメインモデルを中心に設計する。',
|
|
660
|
-
rationale: 'ビジネスロジックの整理とテスタビリティの向上。',
|
|
661
|
-
alternatives: ['トランザクションスクリプト', 'CRUDベース'],
|
|
662
|
-
consequences: [
|
|
663
|
-
'学習コストが発生',
|
|
664
|
-
'エンティティとValue Objectの明確な区別が必要',
|
|
665
|
-
],
|
|
666
|
-
relatedRequirements: ['REQ-ARCH-001'],
|
|
667
|
-
decider: 'Tech Lead',
|
|
668
|
-
});
|
|
669
|
-
```
|
|
670
|
-
|
|
671
|
-
#### `get(id: string): Promise<ADR | undefined>`
|
|
672
|
-
|
|
673
|
-
IDでADRを取得します。
|
|
674
|
-
|
|
675
|
-
```typescript
|
|
676
|
-
const adr = await manager.get('0001');
|
|
677
|
-
if (adr) {
|
|
678
|
-
console.log(adr.title); // => 'JWT認証の採用'
|
|
679
|
-
console.log(adr.status); // => 'proposed'
|
|
680
|
-
}
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
#### `list(filter?: ADRFilter): Promise<ADR[]>`
|
|
684
|
-
|
|
685
|
-
ADR一覧を取得します。
|
|
686
|
-
|
|
687
|
-
```typescript
|
|
688
|
-
interface ADRFilter {
|
|
689
|
-
status?: ADRStatus; // ステータスでフィルタ
|
|
690
|
-
keyword?: string; // キーワード検索
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// 全ADR
|
|
694
|
-
const all = await manager.list();
|
|
695
|
-
|
|
696
|
-
// 承認済みのみ
|
|
697
|
-
const accepted = await manager.list({ status: 'accepted' });
|
|
698
|
-
|
|
699
|
-
// キーワード検索
|
|
700
|
-
const authRelated = await manager.list({ keyword: '認証' });
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
#### `update(id: string, updates: Partial<ADR>): Promise<ADR>`
|
|
704
|
-
|
|
705
|
-
ADRを更新します。
|
|
706
|
-
|
|
707
|
-
```typescript
|
|
708
|
-
await manager.update('0001', {
|
|
709
|
-
rationale: '追加の理由: セキュリティ監査でも推奨された。',
|
|
710
|
-
});
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
#### `accept(id: string): Promise<ADR>`
|
|
714
|
-
|
|
715
|
-
ADRを承認します(proposed → accepted)。
|
|
716
|
-
|
|
717
|
-
```typescript
|
|
718
|
-
const adr = await manager.accept('0001');
|
|
719
|
-
console.log(adr.status); // => 'accepted'
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
#### `deprecate(id: string, supersededBy?: string): Promise<ADR>`
|
|
723
|
-
|
|
724
|
-
ADRを非推奨にします。
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
// 単純な非推奨
|
|
728
|
-
await manager.deprecate('0001');
|
|
729
|
-
|
|
730
|
-
// 別のADRに置き換え
|
|
731
|
-
await manager.deprecate('0001', '0005');
|
|
732
|
-
// 0001のステータスは 'superseded' になり、0005がリンクされる
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
#### `search(query: string): Promise<ADR[]>`
|
|
736
|
-
|
|
737
|
-
キーワードでADRを検索します。
|
|
738
|
-
|
|
739
|
-
```typescript
|
|
740
|
-
const results = await manager.search('認証');
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
#### `findByRequirement(reqId: string): Promise<ADR[]>`
|
|
744
|
-
|
|
745
|
-
要件IDに関連するADRを検索します。
|
|
746
|
-
|
|
747
|
-
```typescript
|
|
748
|
-
const adrs = await manager.findByRequirement('REQ-AUTH-001');
|
|
749
|
-
for (const adr of adrs) {
|
|
750
|
-
console.log(`ADR-${adr.id}: ${adr.title}`);
|
|
751
|
-
}
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
#### `generateIndex(): Promise<void>`
|
|
755
|
-
|
|
756
|
-
ADRインデックス(index.md)を生成します。
|
|
757
|
-
|
|
758
|
-
```typescript
|
|
759
|
-
await manager.generateIndex();
|
|
760
|
-
// docs/decisions/index.md が生成される
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
### ADRテンプレート
|
|
764
|
-
|
|
765
|
-
`@musubix/decisions` には標準テンプレートがエクスポートされています:
|
|
766
|
-
|
|
767
|
-
```typescript
|
|
768
|
-
import { ADR_TEMPLATE } from '@musubix/decisions';
|
|
769
|
-
|
|
770
|
-
console.log(ADR_TEMPLATE);
|
|
771
|
-
// 標準ADRテンプレートが出力される
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
---
|
|
775
|
-
|
|
776
|
-
## 統合ユースケース
|
|
777
|
-
|
|
778
|
-
### ユースケース1: プロジェクト初期化
|
|
779
|
-
|
|
780
|
-
```typescript
|
|
781
|
-
import { knowledge, policy, decisions } from 'musubix';
|
|
782
|
-
|
|
783
|
-
async function initializeProject(projectPath: string) {
|
|
784
|
-
// 1. Knowledge Store 初期化
|
|
785
|
-
const store = knowledge.createKnowledgeStore(`${projectPath}/.knowledge`);
|
|
786
|
-
|
|
787
|
-
// 2. 初期エンティティを作成
|
|
788
|
-
await store.putEntity({
|
|
789
|
-
id: 'PROJECT-META',
|
|
790
|
-
type: 'project',
|
|
791
|
-
name: 'My Project',
|
|
792
|
-
properties: { createdAt: new Date().toISOString() },
|
|
793
|
-
tags: ['root'],
|
|
794
|
-
});
|
|
795
|
-
await store.save();
|
|
796
|
-
|
|
797
|
-
// 3. ポリシー検証
|
|
798
|
-
const engine = policy.createPolicyEngine();
|
|
799
|
-
const report = await engine.validateProject(projectPath);
|
|
800
|
-
|
|
801
|
-
if (!report.passed) {
|
|
802
|
-
console.log('⚠️ プロジェクト構造を調整してください:');
|
|
803
|
-
for (const v of report.violations) {
|
|
804
|
-
console.log(` - ${v.message}`);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// 4. ADRディレクトリ初期化
|
|
809
|
-
const manager = decisions.createDecisionManager(`${projectPath}/docs/decisions`);
|
|
810
|
-
await manager.generateIndex();
|
|
811
|
-
|
|
812
|
-
console.log('✅ プロジェクト初期化完了');
|
|
813
|
-
}
|
|
814
|
-
```
|
|
815
|
-
|
|
816
|
-
### ユースケース2: 要件-設計-タスクのトレーサビリティ管理
|
|
817
|
-
|
|
818
|
-
```typescript
|
|
819
|
-
import { knowledge } from 'musubix';
|
|
820
|
-
|
|
821
|
-
async function createTraceableFeature(store: knowledge.KnowledgeStore) {
|
|
822
|
-
// 要件
|
|
823
|
-
const req = {
|
|
824
|
-
id: 'REQ-PAY-001',
|
|
825
|
-
type: 'requirement',
|
|
826
|
-
name: '支払い機能',
|
|
827
|
-
properties: {
|
|
828
|
-
ears: 'WHEN user confirms order, THE system SHALL process payment',
|
|
829
|
-
priority: 'P0',
|
|
830
|
-
},
|
|
831
|
-
tags: ['payment', 'core'],
|
|
832
|
-
};
|
|
833
|
-
|
|
834
|
-
// 設計
|
|
835
|
-
const design = {
|
|
836
|
-
id: 'DES-PAY-001',
|
|
837
|
-
type: 'design',
|
|
838
|
-
name: '支払いフロー設計',
|
|
839
|
-
properties: {
|
|
840
|
-
c4Level: 'component',
|
|
841
|
-
patterns: ['Strategy', 'Adapter'],
|
|
842
|
-
},
|
|
843
|
-
tags: ['payment'],
|
|
844
|
-
};
|
|
845
|
-
|
|
846
|
-
// タスク群
|
|
847
|
-
const tasks = [
|
|
848
|
-
{ id: 'TSK-PAY-001', name: 'PaymentService実装', estimate: '4h' },
|
|
849
|
-
{ id: 'TSK-PAY-002', name: 'StripeAdapter実装', estimate: '3h' },
|
|
850
|
-
{ id: 'TSK-PAY-003', name: 'テスト作成', estimate: '2h' },
|
|
851
|
-
];
|
|
852
|
-
|
|
853
|
-
// エンティティ追加
|
|
854
|
-
await store.putEntity(req);
|
|
855
|
-
await store.putEntity(design);
|
|
856
|
-
for (const task of tasks) {
|
|
857
|
-
await store.putEntity({
|
|
858
|
-
id: task.id,
|
|
859
|
-
type: 'task',
|
|
860
|
-
name: task.name,
|
|
861
|
-
properties: { estimate: task.estimate },
|
|
862
|
-
tags: ['payment', 'implementation'],
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
// リレーション構築
|
|
867
|
-
await store.addRelation({ source: 'DES-PAY-001', target: 'REQ-PAY-001', type: 'implements' });
|
|
868
|
-
for (const task of tasks) {
|
|
869
|
-
await store.addRelation({ source: task.id, target: 'DES-PAY-001', type: 'realizes' });
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
await store.save();
|
|
873
|
-
|
|
874
|
-
// トレーサビリティ確認
|
|
875
|
-
const trace = await store.traverse('REQ-PAY-001', { direction: 'incoming', maxDepth: 5 });
|
|
876
|
-
console.log('トレーサビリティ:', trace.map(e => `${e.type}:${e.id}`).join(' → '));
|
|
877
|
-
}
|
|
878
|
-
```
|
|
879
|
-
|
|
880
|
-
### ユースケース3: アーキテクチャ決定の記録とポリシー検証
|
|
881
|
-
|
|
882
|
-
```typescript
|
|
883
|
-
import { policy, decisions } from 'musubix';
|
|
884
|
-
|
|
885
|
-
async function recordArchitectureDecision(
|
|
886
|
-
projectPath: string,
|
|
887
|
-
draft: decisions.ADRDraft
|
|
888
|
-
) {
|
|
889
|
-
// 1. ADR作成
|
|
890
|
-
const manager = decisions.createDecisionManager(`${projectPath}/docs/decisions`);
|
|
891
|
-
const adr = await manager.create(draft);
|
|
892
|
-
console.log(`📝 ADR-${adr.id} created: ${adr.title}`);
|
|
893
|
-
|
|
894
|
-
// 2. インデックス更新
|
|
895
|
-
await manager.generateIndex();
|
|
896
|
-
|
|
897
|
-
// 3. ポリシー再検証(CONST-008: Decision Records)
|
|
898
|
-
const engine = policy.createPolicyEngine();
|
|
899
|
-
const report = await engine.validateProject(projectPath, ['CONST-008']);
|
|
900
|
-
|
|
901
|
-
if (report.passed) {
|
|
902
|
-
console.log('✅ CONST-008 (Decision Records) に準拠');
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
return adr;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// 使用例
|
|
909
|
-
await recordArchitectureDecision('.', {
|
|
910
|
-
title: 'TypeScript採用',
|
|
911
|
-
context: '型安全性とDXの向上が必要',
|
|
912
|
-
decision: 'TypeScript 5.x を採用',
|
|
913
|
-
rationale: '静的型付けによるバグ削減とIDEサポートの向上',
|
|
914
|
-
alternatives: ['JavaScript + JSDoc', 'Flow'],
|
|
915
|
-
consequences: ['ビルドステップが必要', '学習コスト'],
|
|
916
|
-
decider: 'Tech Lead',
|
|
917
|
-
});
|
|
918
|
-
```
|
|
919
|
-
|
|
920
|
-
---
|
|
921
|
-
|
|
922
|
-
## CLI統合
|
|
923
|
-
|
|
924
|
-
### musubix CLIからの使用
|
|
925
|
-
|
|
926
|
-
```bash
|
|
927
|
-
# トレーサビリティマトリクス
|
|
928
|
-
npx musubix trace matrix
|
|
929
|
-
|
|
930
|
-
# 要件検証(EARS形式チェック)
|
|
931
|
-
npx musubix requirements validate storage/specs/REQ-001.md
|
|
932
|
-
|
|
933
|
-
# 設計検証
|
|
934
|
-
npx musubix design validate storage/design/DES-001.md
|
|
935
|
-
```
|
|
936
|
-
|
|
937
|
-
### プログラムからのCLI相当処理
|
|
938
|
-
|
|
939
|
-
```typescript
|
|
940
|
-
import { knowledge, policy } from 'musubix';
|
|
941
|
-
|
|
942
|
-
// トレーサビリティマトリクス相当
|
|
943
|
-
async function generateTraceMatrix(store: knowledge.KnowledgeStore) {
|
|
944
|
-
const requirements = await store.query({ type: 'requirement' });
|
|
945
|
-
|
|
946
|
-
console.log('| 要件ID | 設計ID | タスクID | ステータス |');
|
|
947
|
-
console.log('|--------|--------|----------|-----------|');
|
|
948
|
-
|
|
949
|
-
for (const req of requirements) {
|
|
950
|
-
const designs = await store.traverse(req.id, {
|
|
951
|
-
direction: 'incoming',
|
|
952
|
-
relationTypes: ['implements'],
|
|
953
|
-
maxDepth: 1,
|
|
954
|
-
});
|
|
955
|
-
|
|
956
|
-
for (const des of designs.filter(e => e.type === 'design')) {
|
|
957
|
-
const tasks = await store.traverse(des.id, {
|
|
958
|
-
direction: 'incoming',
|
|
959
|
-
relationTypes: ['realizes'],
|
|
960
|
-
maxDepth: 1,
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
console.log(`| ${req.id} | ${des.id} | ${tasks.map(t => t.id).join(', ')} | ✅ |`);
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
```
|
|
968
|
-
|
|
969
|
-
---
|
|
970
|
-
|
|
971
|
-
## トラブルシューティング
|
|
972
|
-
|
|
973
|
-
### Q: `graph.json` がGitで競合した
|
|
974
|
-
|
|
975
|
-
**A:** JSON形式なのでマージツールで解決できます。エンティティは `id` で一意なので、両方のエンティティを保持するマージが安全です。
|
|
976
|
-
|
|
977
|
-
```bash
|
|
978
|
-
# 競合解決後
|
|
979
|
-
git add .knowledge/graph.json
|
|
980
|
-
git commit -m "Merge knowledge graph"
|
|
981
|
-
```
|
|
982
|
-
|
|
983
|
-
### Q: ポリシー検証で常にエラーになる
|
|
984
|
-
|
|
985
|
-
**A:** プロジェクト構造が9憲法条項に準拠していない可能性があります:
|
|
986
|
-
|
|
987
|
-
```bash
|
|
988
|
-
# 必要なディレクトリ構造
|
|
989
|
-
mkdir -p packages bin storage/traceability storage/design steering docs/decisions
|
|
990
|
-
touch vitest.config.ts
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
### Q: ADRのIDが飛んでいる
|
|
994
|
-
|
|
995
|
-
**A:** ADR削除後にIDは再利用されません。これは意図的な設計で、過去の参照を壊さないためです。
|
|
996
|
-
|
|
997
|
-
### Q: traverse() が空配列を返す
|
|
998
|
-
|
|
999
|
-
**A:** リレーションの方向を確認してください:
|
|
1000
|
-
- `outgoing`: 指定エンティティが `source` のリレーション
|
|
1001
|
-
- `incoming`: 指定エンティティが `target` のリレーション
|
|
1002
|
-
|
|
1003
|
-
```typescript
|
|
1004
|
-
// DES-001 → REQ-001 のリレーションがある場合
|
|
1005
|
-
// REQ-001から辿るには incoming を指定
|
|
1006
|
-
const result = await store.traverse('REQ-001', { direction: 'incoming' });
|
|
1007
|
-
```
|
|
1008
|
-
|
|
1009
|
-
---
|
|
1010
|
-
|
|
1011
|
-
## 自然言語による操作(MCP統合)
|
|
1012
|
-
|
|
1013
|
-
MUSUBIX v3.0は、MCP(Model Context Protocol)サーバーを通じてAIエージェント(GitHub Copilot、Claude、Cursor等)から**自然言語で操作**できます。
|
|
1014
|
-
|
|
1015
|
-
### セットアップ
|
|
1016
|
-
|
|
1017
|
-
#### VS Code / GitHub Copilot での設定
|
|
1018
|
-
|
|
1019
|
-
`.vscode/mcp.json` を作成:
|
|
1020
|
-
|
|
1021
|
-
```json
|
|
1022
|
-
{
|
|
1023
|
-
"mcpServers": {
|
|
1024
|
-
"musubix": {
|
|
1025
|
-
"command": "npx",
|
|
1026
|
-
"args": ["@nahisaho/musubix-mcp-server"]
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
```
|
|
1031
|
-
|
|
1032
|
-
#### Claude Desktop での設定
|
|
1033
|
-
|
|
1034
|
-
`claude_desktop_config.json` に追加:
|
|
1035
|
-
|
|
1036
|
-
```json
|
|
1037
|
-
{
|
|
1038
|
-
"mcpServers": {
|
|
1039
|
-
"musubix": {
|
|
1040
|
-
"command": "npx",
|
|
1041
|
-
"args": ["@nahisaho/musubix-mcp-server"]
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
```
|
|
1046
|
-
|
|
1047
|
-
---
|
|
1048
|
-
|
|
1049
|
-
### Knowledge Store(知識グラフ)の自然言語操作
|
|
1050
|
-
|
|
1051
|
-
#### 利用可能なMCPツール
|
|
1052
|
-
|
|
1053
|
-
| ツール | 説明 |
|
|
1054
|
-
|--------|------|
|
|
1055
|
-
| `knowledge_put_entity` | エンティティの作成・更新 |
|
|
1056
|
-
| `knowledge_get_entity` | エンティティの取得 |
|
|
1057
|
-
| `knowledge_delete_entity` | エンティティの削除 |
|
|
1058
|
-
| `knowledge_add_relation` | リレーションの追加 |
|
|
1059
|
-
| `knowledge_query` | フィルタ検索 |
|
|
1060
|
-
| `knowledge_traverse` | グラフ走査 |
|
|
1061
|
-
|
|
1062
|
-
#### 自然言語での使用例
|
|
1063
|
-
|
|
1064
|
-
**エンティティを作成したい場合:**
|
|
1065
|
-
|
|
1066
|
-
```
|
|
1067
|
-
「ユーザー認証」という要件を知識グラフに追加して。
|
|
1068
|
-
IDはREQ-AUTH-001、タグはsecurityとauthで。
|
|
1069
|
-
EARS形式は「WHEN user submits credentials, THE system SHALL authenticate the user」
|
|
1070
|
-
```
|
|
1071
|
-
|
|
1072
|
-
AIエージェントは `knowledge_put_entity` ツールを使用して:
|
|
1073
|
-
```json
|
|
1074
|
-
{
|
|
1075
|
-
"id": "REQ-AUTH-001",
|
|
1076
|
-
"type": "requirement",
|
|
1077
|
-
"name": "ユーザー認証",
|
|
1078
|
-
"properties": {
|
|
1079
|
-
"ears": "WHEN user submits credentials, THE system SHALL authenticate the user"
|
|
1080
|
-
},
|
|
1081
|
-
"tags": ["security", "auth"]
|
|
1082
|
-
}
|
|
1083
|
-
```
|
|
1084
|
-
|
|
1085
|
-
**リレーションを作成したい場合:**
|
|
1086
|
-
|
|
1087
|
-
```
|
|
1088
|
-
DES-AUTH-001がREQ-AUTH-001を実装していることを記録して
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
AIエージェントは `knowledge_add_relation` ツールを使用して:
|
|
1092
|
-
```json
|
|
1093
|
-
{
|
|
1094
|
-
"source": "DES-AUTH-001",
|
|
1095
|
-
"target": "REQ-AUTH-001",
|
|
1096
|
-
"type": "implements"
|
|
1097
|
-
}
|
|
1098
|
-
```
|
|
1099
|
-
|
|
1100
|
-
**検索したい場合:**
|
|
1101
|
-
|
|
1102
|
-
```
|
|
1103
|
-
securityタグが付いた要件を全部見せて
|
|
1104
|
-
```
|
|
1105
|
-
|
|
1106
|
-
AIエージェントは `knowledge_query` ツールを使用して:
|
|
1107
|
-
```json
|
|
1108
|
-
{
|
|
1109
|
-
"type": "requirement",
|
|
1110
|
-
"tags": ["security"]
|
|
1111
|
-
}
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
**トレーサビリティを確認したい場合:**
|
|
1115
|
-
|
|
1116
|
-
```
|
|
1117
|
-
REQ-AUTH-001から辿れる設計とタスクを全部表示して
|
|
1118
|
-
```
|
|
1119
|
-
|
|
1120
|
-
AIエージェントは `knowledge_traverse` ツールを使用して:
|
|
1121
|
-
```json
|
|
1122
|
-
{
|
|
1123
|
-
"startId": "REQ-AUTH-001",
|
|
1124
|
-
"direction": "in",
|
|
1125
|
-
"depth": 5
|
|
1126
|
-
}
|
|
1127
|
-
```
|
|
1128
|
-
|
|
1129
|
-
---
|
|
1130
|
-
|
|
1131
|
-
### Policy Engine(ポリシー検証)の自然言語操作
|
|
1132
|
-
|
|
1133
|
-
#### 利用可能なMCPツール
|
|
1134
|
-
|
|
1135
|
-
| ツール | 説明 |
|
|
1136
|
-
|--------|------|
|
|
1137
|
-
| `policy_validate` | プロジェクト全体の検証 |
|
|
1138
|
-
| `policy_list` | ポリシー一覧取得 |
|
|
1139
|
-
| `policy_get` | ポリシー詳細取得 |
|
|
1140
|
-
| `policy_check_file` | 単一ファイル検証 |
|
|
1141
|
-
|
|
1142
|
-
#### 自然言語での使用例
|
|
1143
|
-
|
|
1144
|
-
**プロジェクトを検証したい場合:**
|
|
1145
|
-
|
|
1146
|
-
```
|
|
1147
|
-
このプロジェクトが9憲法条項に準拠しているかチェックして
|
|
1148
|
-
```
|
|
1149
|
-
|
|
1150
|
-
AIエージェントは `policy_validate` ツールを使用して:
|
|
1151
|
-
```json
|
|
1152
|
-
{
|
|
1153
|
-
"projectPath": "."
|
|
1154
|
-
}
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
**特定のポリシーを確認したい場合:**
|
|
1158
|
-
|
|
1159
|
-
```
|
|
1160
|
-
Library-Firstポリシー(CONST-001)の詳細を教えて
|
|
1161
|
-
```
|
|
1162
|
-
|
|
1163
|
-
AIエージェントは `policy_get` ツールを使用して:
|
|
1164
|
-
```json
|
|
1165
|
-
{
|
|
1166
|
-
"id": "CONST-001"
|
|
1167
|
-
}
|
|
1168
|
-
```
|
|
1169
|
-
|
|
1170
|
-
**要件ファイルを検証したい場合:**
|
|
1171
|
-
|
|
1172
|
-
```
|
|
1173
|
-
storage/specs/REQ-001.mdがEARS形式になっているか確認して
|
|
1174
|
-
```
|
|
1175
|
-
|
|
1176
|
-
AIエージェントは `policy_check_file` ツールを使用して:
|
|
1177
|
-
```json
|
|
1178
|
-
{
|
|
1179
|
-
"filePath": "storage/specs/REQ-001.md"
|
|
1180
|
-
}
|
|
1181
|
-
```
|
|
1182
|
-
|
|
1183
|
-
**全ポリシーを確認したい場合:**
|
|
1184
|
-
|
|
1185
|
-
```
|
|
1186
|
-
登録されているポリシーを一覧で見せて
|
|
1187
|
-
```
|
|
1188
|
-
|
|
1189
|
-
AIエージェントは `policy_list` ツールを使用して:
|
|
1190
|
-
```json
|
|
1191
|
-
{
|
|
1192
|
-
"category": "constitution"
|
|
1193
|
-
}
|
|
1194
|
-
```
|
|
1195
|
-
|
|
1196
|
-
---
|
|
1197
|
-
|
|
1198
|
-
### Decision Records(ADR管理)の自然言語操作
|
|
1199
|
-
|
|
1200
|
-
#### 利用可能なMCPツール
|
|
1201
|
-
|
|
1202
|
-
| ツール | 説明 |
|
|
1203
|
-
|--------|------|
|
|
1204
|
-
| `decision_create` | ADRの作成 |
|
|
1205
|
-
| `decision_list` | ADR一覧取得 |
|
|
1206
|
-
| `decision_get` | ADR詳細取得 |
|
|
1207
|
-
| `decision_accept` | ADRの承認 |
|
|
1208
|
-
| `decision_deprecate` | ADRの非推奨化 |
|
|
1209
|
-
| `decision_search` | キーワード検索 |
|
|
1210
|
-
| `decision_find_by_requirement` | 要件からADR検索 |
|
|
1211
|
-
| `decision_generate_index` | インデックス生成 |
|
|
1212
|
-
|
|
1213
|
-
#### 自然言語での使用例
|
|
1214
|
-
|
|
1215
|
-
**ADRを作成したい場合:**
|
|
1216
|
-
|
|
1217
|
-
```
|
|
1218
|
-
「JWT認証の採用」というADRを作成して。
|
|
1219
|
-
|
|
1220
|
-
コンテキスト: ユーザー認証の仕組みが必要。セッション管理のオーバーヘッドを避けたい。
|
|
1221
|
-
決定: JWTトークンベースの認証を採用する。
|
|
1222
|
-
理由: ステートレスでスケーラブル。マイクロサービス間でも利用可能。
|
|
1223
|
-
代替案: セッションベース認証、OAuth2のみ
|
|
1224
|
-
影響: トークン失効の仕組みが必要、トークンサイズに注意
|
|
1225
|
-
関連要件: REQ-AUTH-001
|
|
1226
|
-
決定者: Tech Lead
|
|
1227
|
-
```
|
|
1228
|
-
|
|
1229
|
-
AIエージェントは `decision_create` ツールを使用して:
|
|
1230
|
-
```json
|
|
1231
|
-
{
|
|
1232
|
-
"title": "JWT認証の採用",
|
|
1233
|
-
"context": "ユーザー認証の仕組みが必要。セッション管理のオーバーヘッドを避けたい。",
|
|
1234
|
-
"decision": "JWTトークンベースの認証を採用する。",
|
|
1235
|
-
"rationale": "ステートレスでスケーラブル。マイクロサービス間でも利用可能。",
|
|
1236
|
-
"alternatives": ["セッションベース認証", "OAuth2のみ"],
|
|
1237
|
-
"consequences": ["トークン失効の仕組みが必要", "トークンサイズに注意"],
|
|
1238
|
-
"relatedRequirements": ["REQ-AUTH-001"],
|
|
1239
|
-
"decider": "Tech Lead"
|
|
1240
|
-
}
|
|
1241
|
-
```
|
|
1242
|
-
|
|
1243
|
-
**ADRを承認したい場合:**
|
|
1244
|
-
|
|
1245
|
-
```
|
|
1246
|
-
ADR-0001を承認して
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
AIエージェントは `decision_accept` ツールを使用して:
|
|
1250
|
-
```json
|
|
1251
|
-
{
|
|
1252
|
-
"id": "0001"
|
|
1253
|
-
}
|
|
1254
|
-
```
|
|
1255
|
-
|
|
1256
|
-
**ADRを検索したい場合:**
|
|
1257
|
-
|
|
1258
|
-
```
|
|
1259
|
-
認証に関するADRを探して
|
|
1260
|
-
```
|
|
1261
|
-
|
|
1262
|
-
AIエージェントは `decision_search` ツールを使用して:
|
|
1263
|
-
```json
|
|
1264
|
-
{
|
|
1265
|
-
"query": "認証"
|
|
1266
|
-
}
|
|
1267
|
-
```
|
|
1268
|
-
|
|
1269
|
-
**要件に関連するADRを探したい場合:**
|
|
1270
|
-
|
|
1271
|
-
```
|
|
1272
|
-
REQ-AUTH-001に関連するアーキテクチャ決定を見せて
|
|
1273
|
-
```
|
|
1274
|
-
|
|
1275
|
-
AIエージェントは `decision_find_by_requirement` ツールを使用して:
|
|
1276
|
-
```json
|
|
1277
|
-
{
|
|
1278
|
-
"requirementId": "REQ-AUTH-001"
|
|
1279
|
-
}
|
|
1280
|
-
```
|
|
1281
|
-
|
|
1282
|
-
**インデックスを更新したい場合:**
|
|
1283
|
-
|
|
1284
|
-
```
|
|
1285
|
-
ADRのインデックスを再生成して
|
|
1286
|
-
```
|
|
1287
|
-
|
|
1288
|
-
AIエージェントは `decision_generate_index` ツールを使用して:
|
|
1289
|
-
```json
|
|
1290
|
-
{}
|
|
1291
|
-
```
|
|
1292
|
-
|
|
1293
|
-
---
|
|
1294
|
-
|
|
1295
|
-
### 統合的な自然言語ワークフロー
|
|
1296
|
-
|
|
1297
|
-
#### 例1: 要件からタスクまでの一連の作業
|
|
1298
|
-
|
|
1299
|
-
```
|
|
1300
|
-
1. 「支払い機能」の要件を追加して(REQ-PAY-001)
|
|
1301
|
-
2. その設計を作成して(DES-PAY-001、Strategyパターンを使用)
|
|
1302
|
-
3. 設計から実装タスクを3つ作成して
|
|
1303
|
-
4. それぞれのトレーサビリティを設定して
|
|
1304
|
-
5. 最後にトレーサビリティチェーンを確認して
|
|
1305
|
-
```
|
|
1306
|
-
|
|
1307
|
-
#### 例2: アーキテクチャ決定の記録と検証
|
|
1308
|
-
|
|
1309
|
-
```
|
|
1310
|
-
1. TypeScript採用についてADRを作成して
|
|
1311
|
-
2. 作成したADRを承認して
|
|
1312
|
-
3. プロジェクトがCONST-008(Decision Records)に準拠しているか確認して
|
|
1313
|
-
```
|
|
1314
|
-
|
|
1315
|
-
#### 例3: コードレビュー前のチェック
|
|
1316
|
-
|
|
1317
|
-
```
|
|
1318
|
-
1. 9憲法条項への準拠をチェックして
|
|
1319
|
-
2. 違反があれば詳細を教えて
|
|
1320
|
-
3. 修正が必要な箇所を提案して
|
|
1321
|
-
```
|
|
1322
|
-
|
|
1323
|
-
---
|
|
1324
|
-
|
|
1325
|
-
### プロンプトテンプレート
|
|
1326
|
-
|
|
1327
|
-
MUSUBIX MCPサーバーには以下のプロンプトテンプレートも用意されています:
|
|
1328
|
-
|
|
1329
|
-
| プロンプト名 | 説明 |
|
|
1330
|
-
|-------------|------|
|
|
1331
|
-
| `sdd_requirements_analysis` | 機能説明からEARS形式要件を生成 |
|
|
1332
|
-
| `sdd_requirements_review` | 要件の完全性・憲法準拠レビュー |
|
|
1333
|
-
| `sdd_design_generation` | 要件からC4モデル設計を生成 |
|
|
1334
|
-
| `synthesis_guidance` | プログラム合成のガイダンス |
|
|
1335
|
-
| `synthesis_explain_pattern` | パターンの説明生成 |
|
|
1336
|
-
|
|
1337
|
-
#### プロンプトの使用例
|
|
1338
|
-
|
|
1339
|
-
```
|
|
1340
|
-
sdd_requirements_analysisプロンプトを使って、
|
|
1341
|
-
「ユーザーがショッピングカートに商品を追加できる」
|
|
1342
|
-
という機能の要件を分析して
|
|
1343
|
-
```
|
|
1344
|
-
|
|
1345
|
-
---
|
|
1346
|
-
|
|
1347
|
-
## 参考リンク
|
|
1348
|
-
|
|
1349
|
-
- [MUSUBIX GitHub](https://github.com/nahisaho/MUSUBIX)
|
|
1350
|
-
- [@musubix/knowledge 詳細マニュアル](./packages/knowledge.md)
|
|
1351
|
-
- [MUSUBIX 9憲法条項](../steering/rules/constitution.md)
|
|
1352
|
-
- [マイグレーションガイド (YATA → Knowledge)](./MIGRATION-v3.0.md)
|
|
1353
|
-
|
|
1354
|
-
---
|
|
1355
|
-
|
|
1356
|
-
**Document Version**: 3.0.0
|
|
1357
|
-
**Last Updated**: 2026-01-12
|