musubi-sdd 5.1.0 → 5.6.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.
Files changed (232) hide show
  1. package/README.ja.md +106 -48
  2. package/README.md +110 -32
  3. package/bin/musubi-analyze.js +74 -67
  4. package/bin/musubi-browser.js +27 -26
  5. package/bin/musubi-change.js +48 -47
  6. package/bin/musubi-checkpoint.js +10 -7
  7. package/bin/musubi-convert.js +25 -25
  8. package/bin/musubi-costs.js +27 -10
  9. package/bin/musubi-gui.js +52 -46
  10. package/bin/musubi-init.js +1952 -10
  11. package/bin/musubi-orchestrate.js +327 -239
  12. package/bin/musubi-remember.js +69 -56
  13. package/bin/musubi-resolve.js +53 -45
  14. package/bin/musubi-trace.js +51 -22
  15. package/bin/musubi-validate.js +39 -30
  16. package/bin/musubi-workflow.js +33 -34
  17. package/bin/musubi.js +39 -2
  18. package/package.json +1 -1
  19. package/src/agents/agent-loop.js +94 -95
  20. package/src/agents/agentic/code-generator.js +119 -109
  21. package/src/agents/agentic/code-reviewer.js +105 -108
  22. package/src/agents/agentic/index.js +4 -4
  23. package/src/agents/browser/action-executor.js +13 -13
  24. package/src/agents/browser/ai-comparator.js +11 -10
  25. package/src/agents/browser/context-manager.js +6 -6
  26. package/src/agents/browser/index.js +5 -5
  27. package/src/agents/browser/nl-parser.js +31 -46
  28. package/src/agents/browser/screenshot.js +2 -2
  29. package/src/agents/browser/test-generator.js +6 -4
  30. package/src/agents/function-tool.js +71 -65
  31. package/src/agents/index.js +7 -7
  32. package/src/agents/schema-generator.js +98 -94
  33. package/src/analyzers/ast-extractor.js +158 -146
  34. package/src/analyzers/codegraph-auto-update.js +858 -0
  35. package/src/analyzers/complexity-analyzer.js +536 -0
  36. package/src/analyzers/context-optimizer.js +241 -126
  37. package/src/analyzers/impact-analyzer.js +1 -1
  38. package/src/analyzers/large-project-analyzer.js +766 -0
  39. package/src/analyzers/repository-map.js +77 -81
  40. package/src/analyzers/security-analyzer.js +19 -11
  41. package/src/analyzers/stuck-detector.js +19 -17
  42. package/src/converters/index.js +78 -57
  43. package/src/converters/ir/types.js +12 -12
  44. package/src/converters/parsers/musubi-parser.js +134 -126
  45. package/src/converters/parsers/openapi-parser.js +70 -53
  46. package/src/converters/parsers/speckit-parser.js +239 -175
  47. package/src/converters/writers/musubi-writer.js +123 -118
  48. package/src/converters/writers/speckit-writer.js +124 -113
  49. package/src/generators/rust-migration-generator.js +512 -0
  50. package/src/gui/public/index.html +1365 -1211
  51. package/src/gui/server.js +41 -40
  52. package/src/gui/services/file-watcher.js +23 -8
  53. package/src/gui/services/project-scanner.js +26 -20
  54. package/src/gui/services/replanning-service.js +27 -23
  55. package/src/gui/services/traceability-service.js +8 -8
  56. package/src/gui/services/workflow-service.js +14 -7
  57. package/src/index.js +151 -0
  58. package/src/integrations/cicd.js +90 -104
  59. package/src/integrations/codegraph-mcp.js +643 -0
  60. package/src/integrations/documentation.js +142 -103
  61. package/src/integrations/examples.js +95 -80
  62. package/src/integrations/github-client.js +17 -17
  63. package/src/integrations/index.js +5 -5
  64. package/src/integrations/mcp/index.js +21 -21
  65. package/src/integrations/mcp/mcp-context-provider.js +76 -78
  66. package/src/integrations/mcp/mcp-discovery.js +74 -72
  67. package/src/integrations/mcp/mcp-tool-registry.js +99 -94
  68. package/src/integrations/mcp-connector.js +70 -66
  69. package/src/integrations/platforms.js +50 -49
  70. package/src/integrations/tool-discovery.js +37 -31
  71. package/src/llm-providers/anthropic-provider.js +11 -11
  72. package/src/llm-providers/base-provider.js +16 -18
  73. package/src/llm-providers/copilot-provider.js +22 -19
  74. package/src/llm-providers/index.js +26 -25
  75. package/src/llm-providers/ollama-provider.js +11 -11
  76. package/src/llm-providers/openai-provider.js +12 -12
  77. package/src/managers/agent-memory.js +36 -24
  78. package/src/managers/checkpoint-manager.js +4 -8
  79. package/src/managers/delta-spec.js +19 -19
  80. package/src/managers/index.js +13 -4
  81. package/src/managers/memory-condenser.js +35 -45
  82. package/src/managers/repo-skill-manager.js +57 -31
  83. package/src/managers/skill-loader.js +25 -22
  84. package/src/managers/skill-tools.js +36 -72
  85. package/src/managers/workflow.js +30 -22
  86. package/src/monitoring/cost-tracker.js +48 -46
  87. package/src/monitoring/incident-manager.js +116 -106
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +75 -62
  90. package/src/monitoring/quality-dashboard.js +45 -41
  91. package/src/monitoring/release-manager.js +63 -53
  92. package/src/orchestration/agent-skill-binding.js +39 -47
  93. package/src/orchestration/error-handler.js +65 -107
  94. package/src/orchestration/guardrails/base-guardrail.js +26 -24
  95. package/src/orchestration/guardrails/guardrail-rules.js +50 -64
  96. package/src/orchestration/guardrails/index.js +5 -5
  97. package/src/orchestration/guardrails/input-guardrail.js +58 -45
  98. package/src/orchestration/guardrails/output-guardrail.js +104 -81
  99. package/src/orchestration/guardrails/safety-check.js +79 -79
  100. package/src/orchestration/index.js +38 -55
  101. package/src/orchestration/mcp-tool-adapters.js +96 -99
  102. package/src/orchestration/orchestration-engine.js +21 -21
  103. package/src/orchestration/pattern-registry.js +60 -45
  104. package/src/orchestration/patterns/auto.js +34 -47
  105. package/src/orchestration/patterns/group-chat.js +59 -65
  106. package/src/orchestration/patterns/handoff.js +67 -65
  107. package/src/orchestration/patterns/human-in-loop.js +51 -72
  108. package/src/orchestration/patterns/nested.js +25 -40
  109. package/src/orchestration/patterns/sequential.js +35 -34
  110. package/src/orchestration/patterns/swarm.js +63 -56
  111. package/src/orchestration/patterns/triage.js +150 -109
  112. package/src/orchestration/reasoning/index.js +9 -9
  113. package/src/orchestration/reasoning/planning-engine.js +143 -140
  114. package/src/orchestration/reasoning/reasoning-engine.js +206 -144
  115. package/src/orchestration/reasoning/self-correction.js +121 -128
  116. package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
  117. package/src/orchestration/replanning/alternative-generator.js +37 -42
  118. package/src/orchestration/replanning/config.js +63 -59
  119. package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
  120. package/src/orchestration/replanning/index.js +24 -20
  121. package/src/orchestration/replanning/plan-evaluator.js +49 -50
  122. package/src/orchestration/replanning/plan-monitor.js +32 -28
  123. package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
  124. package/src/orchestration/replanning/replan-history.js +33 -26
  125. package/src/orchestration/replanning/replanning-engine.js +106 -108
  126. package/src/orchestration/skill-executor.js +107 -109
  127. package/src/orchestration/skill-registry.js +85 -89
  128. package/src/orchestration/workflow-examples.js +228 -231
  129. package/src/orchestration/workflow-executor.js +65 -68
  130. package/src/orchestration/workflow-orchestrator.js +72 -73
  131. package/src/phase4-integration.js +47 -40
  132. package/src/phase5-integration.js +89 -30
  133. package/src/reporters/coverage-report.js +82 -30
  134. package/src/reporters/hierarchical-reporter.js +498 -0
  135. package/src/reporters/traceability-matrix-report.js +29 -20
  136. package/src/resolvers/issue-resolver.js +43 -31
  137. package/src/steering/advanced-validation.js +133 -124
  138. package/src/steering/auto-updater.js +60 -73
  139. package/src/steering/index.js +6 -6
  140. package/src/steering/quality-metrics.js +41 -35
  141. package/src/steering/steering-auto-update.js +83 -86
  142. package/src/steering/steering-validator.js +98 -106
  143. package/src/steering/template-constraints.js +53 -54
  144. package/src/templates/agents/claude-code/CLAUDE.md +32 -32
  145. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
  146. package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
  147. package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
  148. package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
  149. package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
  150. package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
  151. package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
  152. package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
  153. package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
  154. package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
  155. package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
  156. package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
  157. package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
  158. package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
  159. package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
  160. package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
  161. package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
  162. package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
  163. package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
  164. package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
  165. package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
  166. package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
  167. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
  168. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
  169. package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
  170. package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
  171. package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
  172. package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
  173. package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
  174. package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
  175. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
  176. package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
  177. package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
  178. package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
  179. package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
  180. package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
  181. package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
  182. package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
  183. package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
  184. package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
  185. package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
  186. package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
  187. package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
  188. package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
  189. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
  190. package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
  191. package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
  192. package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
  193. package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
  194. package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
  195. package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
  196. package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
  197. package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
  198. package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
  199. package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
  200. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
  201. package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
  202. package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
  203. package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
  204. package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
  205. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
  206. package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
  207. package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
  208. package/src/templates/agents/codex/AGENTS.md +74 -42
  209. package/src/templates/agents/cursor/AGENTS.md +74 -42
  210. package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
  211. package/src/templates/agents/github-copilot/AGENTS.md +83 -51
  212. package/src/templates/agents/qwen-code/QWEN.md +74 -42
  213. package/src/templates/agents/windsurf/AGENTS.md +74 -42
  214. package/src/templates/architectures/README.md +41 -0
  215. package/src/templates/architectures/clean-architecture/README.md +113 -0
  216. package/src/templates/architectures/event-driven/README.md +162 -0
  217. package/src/templates/architectures/hexagonal/README.md +130 -0
  218. package/src/templates/index.js +6 -1
  219. package/src/templates/locale-manager.js +16 -16
  220. package/src/templates/shared/delta-spec-template.md +20 -13
  221. package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
  222. package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
  223. package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
  224. package/src/templates/shared/steering/structure.md +95 -0
  225. package/src/templates/skills/browser-agent.md +21 -16
  226. package/src/templates/skills/web-gui.md +8 -0
  227. package/src/templates/template-constraints.js +50 -53
  228. package/src/validators/advanced-validation.js +30 -36
  229. package/src/validators/constitutional-validator.js +77 -73
  230. package/src/validators/critic-system.js +49 -59
  231. package/src/validators/delta-format.js +59 -55
  232. package/src/validators/traceability-validator.js +7 -11
@@ -13,6 +13,7 @@ This guide describes how to map EARS (Easy Approach to Requirements Syntax) requ
13
13
  **EARS Pattern**: `The [system] SHALL [action]`
14
14
 
15
15
  **Test Template**:
16
+
16
17
  ```typescript
17
18
  describe('[REQ-ID]: Ubiquitous Requirement', () => {
18
19
  it('should always [action]', () => {
@@ -22,6 +23,7 @@ describe('[REQ-ID]: Ubiquitous Requirement', () => {
22
23
  ```
23
24
 
24
25
  **Example**:
26
+
25
27
  ```markdown
26
28
  REQ-SEC-001: The system SHALL encrypt all passwords using bcrypt.
27
29
  ```
@@ -31,15 +33,15 @@ describe('REQ-SEC-001: Password Encryption', () => {
31
33
  it('should encrypt password using bcrypt', () => {
32
34
  const plainPassword = 'testPassword123';
33
35
  const hashedPassword = passwordService.hash(plainPassword);
34
-
36
+
35
37
  expect(hashedPassword).not.toBe(plainPassword);
36
38
  expect(bcrypt.getRounds(hashedPassword)).toBeGreaterThanOrEqual(12);
37
39
  });
38
-
40
+
39
41
  it('should always use bcrypt regardless of user type', () => {
40
42
  const adminPassword = passwordService.hash('adminPass');
41
43
  const userPassword = passwordService.hash('userPass');
42
-
44
+
43
45
  expect(adminPassword).toMatch(/^\$2[aby]?\$/); // bcrypt prefix
44
46
  expect(userPassword).toMatch(/^\$2[aby]?\$/);
45
47
  });
@@ -53,6 +55,7 @@ describe('REQ-SEC-001: Password Encryption', () => {
53
55
  **EARS Pattern**: `WHEN [event], the [system] SHALL [action]`
54
56
 
55
57
  **Test Template**:
58
+
56
59
  ```typescript
57
60
  describe('[REQ-ID]: Event-Driven Requirement', () => {
58
61
  describe('WHEN [event]', () => {
@@ -61,7 +64,7 @@ describe('[REQ-ID]: Event-Driven Requirement', () => {
61
64
  // Verify action occurred
62
65
  });
63
66
  });
64
-
67
+
65
68
  describe('WHEN [event] does not occur', () => {
66
69
  it('should not [action]', () => {
67
70
  // Don't trigger event
@@ -72,6 +75,7 @@ describe('[REQ-ID]: Event-Driven Requirement', () => {
72
75
  ```
73
76
 
74
77
  **Example**:
78
+
75
79
  ```markdown
76
80
  REQ-AUTH-001: WHEN user submits login form with valid credentials, the system SHALL authenticate user and create session.
77
81
  ```
@@ -81,26 +85,26 @@ describe('REQ-AUTH-001: Login Authentication', () => {
81
85
  describe('WHEN user submits login form with valid credentials', () => {
82
86
  it('should authenticate user', async () => {
83
87
  const result = await authService.login('user@test.com', 'validPassword');
84
-
88
+
85
89
  expect(result.success).toBe(true);
86
90
  expect(result.user).toBeDefined();
87
91
  });
88
-
92
+
89
93
  it('should create session', async () => {
90
94
  const result = await authService.login('user@test.com', 'validPassword');
91
-
95
+
92
96
  expect(result.session).toBeDefined();
93
97
  expect(result.session.expiresAt).toBeInstanceOf(Date);
94
98
  });
95
99
  });
96
-
100
+
97
101
  describe('WHEN user submits login form with invalid credentials', () => {
98
102
  it('should NOT authenticate user', async () => {
99
- await expect(
100
- authService.login('user@test.com', 'wrongPassword')
101
- ).rejects.toThrow('Invalid credentials');
103
+ await expect(authService.login('user@test.com', 'wrongPassword')).rejects.toThrow(
104
+ 'Invalid credentials'
105
+ );
102
106
  });
103
-
107
+
104
108
  it('should NOT create session', async () => {
105
109
  try {
106
110
  await authService.login('user@test.com', 'wrongPassword');
@@ -120,23 +124,24 @@ describe('REQ-AUTH-001: Login Authentication', () => {
120
124
  **EARS Pattern**: `WHILE [state], the [system] SHALL [action]`
121
125
 
122
126
  **Test Template**:
127
+
123
128
  ```typescript
124
129
  describe('[REQ-ID]: State-Driven Requirement', () => {
125
130
  describe('WHILE [state] is true', () => {
126
131
  beforeEach(() => {
127
132
  // Set up state
128
133
  });
129
-
134
+
130
135
  it('should [action]', () => {
131
136
  // Verify action
132
137
  });
133
138
  });
134
-
139
+
135
140
  describe('WHILE [state] is false', () => {
136
141
  beforeEach(() => {
137
142
  // Set up opposite state
138
143
  });
139
-
144
+
140
145
  it('should NOT [action]', () => {
141
146
  // Verify action does not occur
142
147
  });
@@ -145,6 +150,7 @@ describe('[REQ-ID]: State-Driven Requirement', () => {
145
150
  ```
146
151
 
147
152
  **Example**:
153
+
148
154
  ```markdown
149
155
  REQ-MAINT-001: WHILE maintenance mode is active, the system SHALL show maintenance page to users.
150
156
  ```
@@ -155,36 +161,36 @@ describe('REQ-MAINT-001: Maintenance Mode', () => {
155
161
  beforeEach(() => {
156
162
  config.set('maintenanceMode', true);
157
163
  });
158
-
164
+
159
165
  afterEach(() => {
160
166
  config.set('maintenanceMode', false);
161
167
  });
162
-
168
+
163
169
  it('should show maintenance page', async () => {
164
170
  const response = await request(app).get('/');
165
-
171
+
166
172
  expect(response.status).toBe(503);
167
173
  expect(response.text).toContain('Maintenance');
168
174
  });
169
-
175
+
170
176
  it('should show maintenance page for all routes', async () => {
171
177
  const routes = ['/', '/products', '/cart', '/account'];
172
-
178
+
173
179
  for (const route of routes) {
174
180
  const response = await request(app).get(route);
175
181
  expect(response.status).toBe(503);
176
182
  }
177
183
  });
178
184
  });
179
-
185
+
180
186
  describe('WHILE maintenance mode is NOT active', () => {
181
187
  beforeEach(() => {
182
188
  config.set('maintenanceMode', false);
183
189
  });
184
-
190
+
185
191
  it('should NOT show maintenance page', async () => {
186
192
  const response = await request(app).get('/');
187
-
193
+
188
194
  expect(response.status).not.toBe(503);
189
195
  expect(response.text).not.toContain('Maintenance');
190
196
  });
@@ -199,23 +205,24 @@ describe('REQ-MAINT-001: Maintenance Mode', () => {
199
205
  **EARS Pattern**: `WHERE [feature] is enabled, the [system] SHALL [action]`
200
206
 
201
207
  **Test Template**:
208
+
202
209
  ```typescript
203
210
  describe('[REQ-ID]: Optional Feature Requirement', () => {
204
211
  describe('WHERE [feature] is enabled', () => {
205
212
  beforeEach(() => {
206
213
  // Enable feature
207
214
  });
208
-
215
+
209
216
  it('should [action]', () => {
210
217
  // Verify action
211
218
  });
212
219
  });
213
-
220
+
214
221
  describe('WHERE [feature] is NOT enabled', () => {
215
222
  beforeEach(() => {
216
223
  // Disable feature
217
224
  });
218
-
225
+
219
226
  it('should NOT [action]', () => {
220
227
  // Verify action does not occur
221
228
  });
@@ -224,6 +231,7 @@ describe('[REQ-ID]: Optional Feature Requirement', () => {
224
231
  ```
225
232
 
226
233
  **Example**:
234
+
227
235
  ```markdown
228
236
  REQ-2FA-001: WHERE two-factor authentication is enabled, the system SHALL require OTP after password verification.
229
237
  ```
@@ -232,39 +240,39 @@ REQ-2FA-001: WHERE two-factor authentication is enabled, the system SHALL requir
232
240
  describe('REQ-2FA-001: Two-Factor Authentication', () => {
233
241
  describe('WHERE 2FA is enabled for user', () => {
234
242
  let user: User;
235
-
243
+
236
244
  beforeEach(async () => {
237
245
  user = await createUser({ twoFactorEnabled: true });
238
246
  });
239
-
247
+
240
248
  it('should require OTP after password verification', async () => {
241
249
  const result = await authService.login(user.email, 'password');
242
-
250
+
243
251
  expect(result.requiresOtp).toBe(true);
244
252
  expect(result.authenticated).toBe(false);
245
253
  expect(result.otpSent).toBe(true);
246
254
  });
247
-
255
+
248
256
  it('should authenticate only after valid OTP', async () => {
249
257
  const loginResult = await authService.login(user.email, 'password');
250
258
  const otp = await getOtpForUser(user);
251
-
259
+
252
260
  const finalResult = await authService.verifyOtp(loginResult.token, otp);
253
-
261
+
254
262
  expect(finalResult.authenticated).toBe(true);
255
263
  });
256
264
  });
257
-
265
+
258
266
  describe('WHERE 2FA is NOT enabled for user', () => {
259
267
  let user: User;
260
-
268
+
261
269
  beforeEach(async () => {
262
270
  user = await createUser({ twoFactorEnabled: false });
263
271
  });
264
-
272
+
265
273
  it('should NOT require OTP', async () => {
266
274
  const result = await authService.login(user.email, 'password');
267
-
275
+
268
276
  expect(result.requiresOtp).toBe(false);
269
277
  expect(result.authenticated).toBe(true);
270
278
  });
@@ -279,6 +287,7 @@ describe('REQ-2FA-001: Two-Factor Authentication', () => {
279
287
  **EARS Pattern**: `IF [condition], THEN the [system] SHALL [response]`
280
288
 
281
289
  **Test Template**:
290
+
282
291
  ```typescript
283
292
  describe('[REQ-ID]: Unwanted Behavior Requirement', () => {
284
293
  describe('IF [unwanted condition] occurs', () => {
@@ -287,7 +296,7 @@ describe('[REQ-ID]: Unwanted Behavior Requirement', () => {
287
296
  // Verify response
288
297
  });
289
298
  });
290
-
299
+
291
300
  describe('IF [unwanted condition] does NOT occur', () => {
292
301
  it('should proceed normally', () => {
293
302
  // Normal flow
@@ -297,6 +306,7 @@ describe('[REQ-ID]: Unwanted Behavior Requirement', () => {
297
306
  ```
298
307
 
299
308
  **Example**:
309
+
300
310
  ```markdown
301
311
  REQ-LOCK-001: IF login fails 5 consecutive times, THEN the system SHALL lock account for 30 minutes.
302
312
  ```
@@ -305,58 +315,52 @@ REQ-LOCK-001: IF login fails 5 consecutive times, THEN the system SHALL lock acc
305
315
  describe('REQ-LOCK-001: Account Lockout', () => {
306
316
  describe('IF login fails 5 consecutive times', () => {
307
317
  let user: User;
308
-
318
+
309
319
  beforeEach(async () => {
310
320
  user = await createUser();
311
321
  });
312
-
322
+
313
323
  it('should lock account for 30 minutes', async () => {
314
324
  // Fail 5 times
315
325
  for (let i = 0; i < 5; i++) {
316
- await expect(
317
- authService.login(user.email, 'wrongPassword')
318
- ).rejects.toThrow();
326
+ await expect(authService.login(user.email, 'wrongPassword')).rejects.toThrow();
319
327
  }
320
-
328
+
321
329
  // Verify lockout
322
- await expect(
323
- authService.login(user.email, 'correctPassword')
324
- ).rejects.toThrow('Account locked');
325
-
330
+ await expect(authService.login(user.email, 'correctPassword')).rejects.toThrow(
331
+ 'Account locked'
332
+ );
333
+
326
334
  // Verify 30 minute lockout
327
335
  const lockedUser = await userRepository.findByEmail(user.email);
328
336
  const lockDuration = lockedUser.lockedUntil - Date.now();
329
337
  expect(lockDuration).toBeCloseTo(30 * 60 * 1000, -4); // 30 min ± 10 sec
330
338
  });
331
-
339
+
332
340
  it('should allow login after 30 minutes', async () => {
333
341
  // Fail 5 times
334
342
  for (let i = 0; i < 5; i++) {
335
- await expect(
336
- authService.login(user.email, 'wrongPassword')
337
- ).rejects.toThrow();
343
+ await expect(authService.login(user.email, 'wrongPassword')).rejects.toThrow();
338
344
  }
339
-
345
+
340
346
  // Time travel 30 minutes
341
347
  jest.advanceTimersByTime(30 * 60 * 1000);
342
-
348
+
343
349
  // Should work now
344
350
  const result = await authService.login(user.email, 'correctPassword');
345
351
  expect(result.authenticated).toBe(true);
346
352
  });
347
353
  });
348
-
354
+
349
355
  describe('IF login fails less than 5 times', () => {
350
356
  it('should NOT lock account', async () => {
351
357
  const user = await createUser();
352
-
358
+
353
359
  // Fail 4 times
354
360
  for (let i = 0; i < 4; i++) {
355
- await expect(
356
- authService.login(user.email, 'wrongPassword')
357
- ).rejects.toThrow();
361
+ await expect(authService.login(user.email, 'wrongPassword')).rejects.toThrow();
358
362
  }
359
-
363
+
360
364
  // Should still work with correct password
361
365
  const result = await authService.login(user.email, 'correctPassword');
362
366
  expect(result.authenticated).toBe(true);
@@ -372,13 +376,13 @@ describe('REQ-LOCK-001: Account Lockout', () => {
372
376
  ```markdown
373
377
  # Test Traceability Matrix
374
378
 
375
- | REQ ID | EARS Pattern | Test File | Test Names | Status |
376
- |--------|--------------|-----------|------------|--------|
377
- | REQ-AUTH-001 | Event-driven | auth.test.ts | login tests | ✅ |
378
- | REQ-SEC-001 | Ubiquitous | security.test.ts | encryption tests | ✅ |
379
- | REQ-2FA-001 | Optional | 2fa.test.ts | 2FA tests | ✅ |
380
- | REQ-LOCK-001 | Unwanted | lockout.test.ts | lockout tests | ✅ |
381
- | REQ-MAINT-001 | State-driven | maintenance.test.ts | mode tests | ✅ |
379
+ | REQ ID | EARS Pattern | Test File | Test Names | Status |
380
+ | ------------- | ------------ | ------------------- | ---------------- | ------ |
381
+ | REQ-AUTH-001 | Event-driven | auth.test.ts | login tests | ✅ |
382
+ | REQ-SEC-001 | Ubiquitous | security.test.ts | encryption tests | ✅ |
383
+ | REQ-2FA-001 | Optional | 2fa.test.ts | 2FA tests | ✅ |
384
+ | REQ-LOCK-001 | Unwanted | lockout.test.ts | lockout tests | ✅ |
385
+ | REQ-MAINT-001 | State-driven | maintenance.test.ts | mode tests | ✅ |
382
386
 
383
387
  ## Coverage Summary
384
388
 
@@ -394,7 +398,7 @@ describe('REQ-LOCK-001: Account Lockout', () => {
394
398
  ```typescript
395
399
  /**
396
400
  * @requirement REQ-AUTH-001
397
- * @ears WHEN user submits login form with valid credentials,
401
+ * @ears WHEN user submits login form with valid credentials,
398
402
  * the system SHALL authenticate user and create session.
399
403
  * @pattern Event-driven
400
404
  * @priority P1
@@ -430,15 +434,15 @@ def extract_tested_requirements(test_dir):
430
434
  def check_coverage(req_file, test_dir):
431
435
  requirements = extract_requirements(req_file)
432
436
  tested = extract_tested_requirements(test_dir)
433
-
437
+
434
438
  untested = requirements - tested
435
439
  orphaned = tested - requirements
436
-
440
+
437
441
  coverage = len(tested & requirements) / len(requirements) * 100
438
-
442
+
439
443
  print(f"Coverage: {coverage:.1f}%")
440
444
  print(f"Untested: {untested}")
441
445
  print(f"Orphaned: {orphaned}")
442
-
446
+
443
447
  return coverage == 100
444
448
  ```