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.
- package/README.ja.md +106 -48
- package/README.md +110 -32
- package/bin/musubi-analyze.js +74 -67
- package/bin/musubi-browser.js +27 -26
- package/bin/musubi-change.js +48 -47
- package/bin/musubi-checkpoint.js +10 -7
- package/bin/musubi-convert.js +25 -25
- package/bin/musubi-costs.js +27 -10
- package/bin/musubi-gui.js +52 -46
- package/bin/musubi-init.js +1952 -10
- package/bin/musubi-orchestrate.js +327 -239
- package/bin/musubi-remember.js +69 -56
- package/bin/musubi-resolve.js +53 -45
- package/bin/musubi-trace.js +51 -22
- package/bin/musubi-validate.js +39 -30
- package/bin/musubi-workflow.js +33 -34
- package/bin/musubi.js +39 -2
- package/package.json +1 -1
- package/src/agents/agent-loop.js +94 -95
- package/src/agents/agentic/code-generator.js +119 -109
- package/src/agents/agentic/code-reviewer.js +105 -108
- package/src/agents/agentic/index.js +4 -4
- package/src/agents/browser/action-executor.js +13 -13
- package/src/agents/browser/ai-comparator.js +11 -10
- package/src/agents/browser/context-manager.js +6 -6
- package/src/agents/browser/index.js +5 -5
- package/src/agents/browser/nl-parser.js +31 -46
- package/src/agents/browser/screenshot.js +2 -2
- package/src/agents/browser/test-generator.js +6 -4
- package/src/agents/function-tool.js +71 -65
- package/src/agents/index.js +7 -7
- package/src/agents/schema-generator.js +98 -94
- package/src/analyzers/ast-extractor.js +158 -146
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +241 -126
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +77 -81
- package/src/analyzers/security-analyzer.js +19 -11
- package/src/analyzers/stuck-detector.js +19 -17
- package/src/converters/index.js +78 -57
- package/src/converters/ir/types.js +12 -12
- package/src/converters/parsers/musubi-parser.js +134 -126
- package/src/converters/parsers/openapi-parser.js +70 -53
- package/src/converters/parsers/speckit-parser.js +239 -175
- package/src/converters/writers/musubi-writer.js +123 -118
- package/src/converters/writers/speckit-writer.js +124 -113
- package/src/generators/rust-migration-generator.js +512 -0
- package/src/gui/public/index.html +1365 -1211
- package/src/gui/server.js +41 -40
- package/src/gui/services/file-watcher.js +23 -8
- package/src/gui/services/project-scanner.js +26 -20
- package/src/gui/services/replanning-service.js +27 -23
- package/src/gui/services/traceability-service.js +8 -8
- package/src/gui/services/workflow-service.js +14 -7
- package/src/index.js +151 -0
- package/src/integrations/cicd.js +90 -104
- package/src/integrations/codegraph-mcp.js +643 -0
- package/src/integrations/documentation.js +142 -103
- package/src/integrations/examples.js +95 -80
- package/src/integrations/github-client.js +17 -17
- package/src/integrations/index.js +5 -5
- package/src/integrations/mcp/index.js +21 -21
- package/src/integrations/mcp/mcp-context-provider.js +76 -78
- package/src/integrations/mcp/mcp-discovery.js +74 -72
- package/src/integrations/mcp/mcp-tool-registry.js +99 -94
- package/src/integrations/mcp-connector.js +70 -66
- package/src/integrations/platforms.js +50 -49
- package/src/integrations/tool-discovery.js +37 -31
- package/src/llm-providers/anthropic-provider.js +11 -11
- package/src/llm-providers/base-provider.js +16 -18
- package/src/llm-providers/copilot-provider.js +22 -19
- package/src/llm-providers/index.js +26 -25
- package/src/llm-providers/ollama-provider.js +11 -11
- package/src/llm-providers/openai-provider.js +12 -12
- package/src/managers/agent-memory.js +36 -24
- package/src/managers/checkpoint-manager.js +4 -8
- package/src/managers/delta-spec.js +19 -19
- package/src/managers/index.js +13 -4
- package/src/managers/memory-condenser.js +35 -45
- package/src/managers/repo-skill-manager.js +57 -31
- package/src/managers/skill-loader.js +25 -22
- package/src/managers/skill-tools.js +36 -72
- package/src/managers/workflow.js +30 -22
- package/src/monitoring/cost-tracker.js +48 -46
- package/src/monitoring/incident-manager.js +116 -106
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +75 -62
- package/src/monitoring/quality-dashboard.js +45 -41
- package/src/monitoring/release-manager.js +63 -53
- package/src/orchestration/agent-skill-binding.js +39 -47
- package/src/orchestration/error-handler.js +65 -107
- package/src/orchestration/guardrails/base-guardrail.js +26 -24
- package/src/orchestration/guardrails/guardrail-rules.js +50 -64
- package/src/orchestration/guardrails/index.js +5 -5
- package/src/orchestration/guardrails/input-guardrail.js +58 -45
- package/src/orchestration/guardrails/output-guardrail.js +104 -81
- package/src/orchestration/guardrails/safety-check.js +79 -79
- package/src/orchestration/index.js +38 -55
- package/src/orchestration/mcp-tool-adapters.js +96 -99
- package/src/orchestration/orchestration-engine.js +21 -21
- package/src/orchestration/pattern-registry.js +60 -45
- package/src/orchestration/patterns/auto.js +34 -47
- package/src/orchestration/patterns/group-chat.js +59 -65
- package/src/orchestration/patterns/handoff.js +67 -65
- package/src/orchestration/patterns/human-in-loop.js +51 -72
- package/src/orchestration/patterns/nested.js +25 -40
- package/src/orchestration/patterns/sequential.js +35 -34
- package/src/orchestration/patterns/swarm.js +63 -56
- package/src/orchestration/patterns/triage.js +150 -109
- package/src/orchestration/reasoning/index.js +9 -9
- package/src/orchestration/reasoning/planning-engine.js +143 -140
- package/src/orchestration/reasoning/reasoning-engine.js +206 -144
- package/src/orchestration/reasoning/self-correction.js +121 -128
- package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
- package/src/orchestration/replanning/alternative-generator.js +37 -42
- package/src/orchestration/replanning/config.js +63 -59
- package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
- package/src/orchestration/replanning/index.js +24 -20
- package/src/orchestration/replanning/plan-evaluator.js +49 -50
- package/src/orchestration/replanning/plan-monitor.js +32 -28
- package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
- package/src/orchestration/replanning/replan-history.js +33 -26
- package/src/orchestration/replanning/replanning-engine.js +106 -108
- package/src/orchestration/skill-executor.js +107 -109
- package/src/orchestration/skill-registry.js +85 -89
- package/src/orchestration/workflow-examples.js +228 -231
- package/src/orchestration/workflow-executor.js +65 -68
- package/src/orchestration/workflow-orchestrator.js +72 -73
- package/src/phase4-integration.js +47 -40
- package/src/phase5-integration.js +89 -30
- package/src/reporters/coverage-report.js +82 -30
- package/src/reporters/hierarchical-reporter.js +498 -0
- package/src/reporters/traceability-matrix-report.js +29 -20
- package/src/resolvers/issue-resolver.js +43 -31
- package/src/steering/advanced-validation.js +133 -124
- package/src/steering/auto-updater.js +60 -73
- package/src/steering/index.js +6 -6
- package/src/steering/quality-metrics.js +41 -35
- package/src/steering/steering-auto-update.js +83 -86
- package/src/steering/steering-validator.js +98 -106
- package/src/steering/template-constraints.js +53 -54
- package/src/templates/agents/claude-code/CLAUDE.md +32 -32
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
- package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
- package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
- package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
- package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
- package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
- package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
- package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
- package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
- package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
- package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
- package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
- package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
- package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
- package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
- package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
- package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
- package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
- package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
- package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
- package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
- package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
- package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
- package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
- package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
- package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
- package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
- package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
- package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
- package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
- package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
- package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
- package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
- package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
- package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
- package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
- package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
- package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
- package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
- package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
- package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
- package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
- package/src/templates/agents/codex/AGENTS.md +74 -42
- package/src/templates/agents/cursor/AGENTS.md +74 -42
- package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
- package/src/templates/agents/github-copilot/AGENTS.md +83 -51
- package/src/templates/agents/qwen-code/QWEN.md +74 -42
- package/src/templates/agents/windsurf/AGENTS.md +74 -42
- package/src/templates/architectures/README.md +41 -0
- package/src/templates/architectures/clean-architecture/README.md +113 -0
- package/src/templates/architectures/event-driven/README.md +162 -0
- package/src/templates/architectures/hexagonal/README.md +130 -0
- package/src/templates/index.js +6 -1
- package/src/templates/locale-manager.js +16 -16
- package/src/templates/shared/delta-spec-template.md +20 -13
- package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
- package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
- package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
- package/src/templates/shared/steering/structure.md +95 -0
- package/src/templates/skills/browser-agent.md +21 -16
- package/src/templates/skills/web-gui.md +8 -0
- package/src/templates/template-constraints.js +50 -53
- package/src/validators/advanced-validation.js +30 -36
- package/src/validators/constitutional-validator.js +77 -73
- package/src/validators/critic-system.js +49 -59
- package/src/validators/delta-format.js +59 -55
- 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
|
-
|
|
101
|
-
)
|
|
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
|
-
|
|
324
|
-
)
|
|
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
|
|
376
|
-
|
|
377
|
-
| REQ-AUTH-001
|
|
378
|
-
| REQ-SEC-001
|
|
379
|
-
| REQ-2FA-001
|
|
380
|
-
| REQ-LOCK-001
|
|
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
|
```
|