Prompt file imported from VEaswaran/MonoFluxDemoApplication (
.windsurf/workflows/nfr-report.md). Copyright stays with the author.
/nfr-report
Trigger: /nfr-report
Description: Generate a structured NFR Impact Report based on the findings from /nfr-check.
Documents all impacted APIs, risk classifications, estimated performance impact, and
recommended remediations. Output saved as a markdown file and appended to the PR description.
Steps
1. Load NFR Check Results
Read the output files from /nfr-check:
/tmp/nfr-loops.txt/tmp/nfr-api-calls.txt/tmp/nfr-unbounded-queries.txt/tmp/nfr-transactional-api.txt/tmp/nfr-blocking.txt/tmp/nfr-impacted-apis.txt
If these files are missing, ask the user to run /nfr-check first.
2. Build the Full NFR Impact Report
Generate the file nfr-impact-report-[STORY-ID].md in the repo root:
# NFR Impact Report ā [STORY-ID]: [Story Title]
**Date:** [today's date]
**Branch:** [current branch]
**Author:** [git config user.name]
**Reviewed by Cascade:** Yes
---
## 1. Executive Summary
| Metric | Value |
|--------|-------|
| Files changed | [N] |
| Methods changed | [N] |
| NFR patterns detected | [N] |
| Impacted API endpoints | [N] |
| CRITICAL blockers | [N] |
| HIGH risk items | [N] |
| MEDIUM risk items | [N] |
| Safe to merge | YES / NO |
**Overall NFR Risk Level:** š“ CRITICAL / š HIGH / š” MEDIUM / š¢ LOW
---
## 2. Impacted API Endpoints
For each controller endpoint that calls a changed service method:
| # | Method | Path | SLA Target | Estimated Latency Impact | Risk |
|---|--------|------|-----------|--------------------------|------|
| 1 | POST | /orders | < 200ms | +[X]ms per loop iteration Ć N items | š“ CRITICAL |
| 2 | GET | /products | < 100ms | Unbounded query ā O(N) memory | š HIGH |
| 3 | POST | /notifications | < 500ms | Holds DB conn during API call | š“ CRITICAL |
**How latency impact is estimated:**
- Loop with API call: assume each FeignClient call = 50ā200ms RTT. If collection = 100 items ā +5,000ā20,000ms
- Unbounded query: assume 10k rows = ~100ā500ms + GC pressure
- API call inside @Transactional: DB connection held for full RTT of external call (50ā500ms added to conn hold time)
---
## 3. Detailed NFR Findings
### Finding 1 ā [NFR Pattern Name]
| Field | Value |
|-------|-------|
| **File** | `[FileName].java` |
| **Method** | `[methodName]()` |
| **Line** | [line number in diff] |
| **NFR Category** | Latency / Throughput / Resource / Reliability |
| **Pattern** | [describe the pattern e.g. "FeignClient call inside forEach loop"] |
| **Risk Level** | š“ CRITICAL |
| **Blocker** | YES |
**Current code (diff excerpt):**
```java
// [paste the relevant + lines from the diff]
Impact:
- Each call to
[ExternalService].fetch()adds ~[X]ms network RTT - With [N] items in loop ā total added latency: ~[X * N]ms in worst case
- Under 100 concurrent requests ā [100 Ć X Ć N]ms of thread-held time
- At Spring Boot default thread pool (200 threads): exhaustion risk at [threshold] RPS
Remediation:
// Option A ā Batch the external call
List<String> ids = items.stream().map(Item::getId).toList();
List<Result> results = externalClient.fetchBatch(ids); // single API call
// Option B ā Cache with @Cacheable if data is stable
@Cacheable(value = "items", key = "#id")
public Item getItem(String id) { ... }
// Option C ā Async parallel with CompletableFuture (non-transactional context only)
List<CompletableFuture<Result>> futures = items.stream()
.map(item -> CompletableFuture.supplyAsync(() -> client.fetch(item.getId()), executor))
.toList();
List<Result> results = futures.stream().map(CompletableFuture::join).toList();
Spock test to add (regression guard):
def "should call external API once for all items, not once per item"() {
given:
def items = (1..10).collect { new Item(id: "item-$it") }
when:
service.processItems(items)
then:
// Assert batch call, not 10 individual calls
1 * mockClient.fetchBatch(_)
0 * mockClient.fetch(_)
}
Finding 2 ā [Next Finding]
[repeat block above for each finding from /nfr-check]
4. Call Chain Diagram
For each impacted API, show the full call chain with NFR risk annotations:
POST /orders
āā OrderController.createOrder()
āā OrderService.processOrder() ā CHANGED
āā for each orderLine (N items)
ā āā PricingClient.getPrice() ā š“ API CALL IN LOOP
ā āā InventoryRepo.findById() ā š“ N+1 QUERY
āā OrderRepo.save() ā OK
GET /products
āā ProductController.listProducts()
āā ProductService.findAll() ā CHANGED
āā ProductRepo.findAll() ā š UNBOUNDED LIST
5. Resource Impact Estimates
Thread Pool (Tomcat default: 200 threads)
| Scenario | Added hold time per req | Saturation RPS (200 threads) |
|---|---|---|
| Loop (N=50) Ć FeignClient (100ms) | 5,000ms | 200/5 = 40 RPS š“ |
| Single FeignClient (100ms) | 100ms | 200/0.1 = 2,000 RPS ā |
| Unbounded query (10k rows) | 400ms | 200/0.4 = 500 RPS š” |
DB Connection Pool (HikariCP default: 10 connections)
| Scenario | Hold time per conn | Saturation RPS |
|---|---|---|
| @Transactional + API call (200ms) | 200ms | 10/0.2 = 50 RPS š“ |
| Normal @Transactional (10ms) | 10ms | 10/0.01 = 1,000 RPS ā |
Memory
| Scenario | Estimated heap per request | Risk |
|---|---|---|
| findAll() returning 10k rows | ~50ā100 MB | š HIGH ā GC pressure |
| Paginated (page size 20) | ~1 MB | ā OK |
6. Recommended Actions
| Priority | Action | Owner | Blocker? |
|---|---|---|---|
| P0 š“ | Replace loop+API with batch FeignClient call in OrderService |
Dev | YES |
| P0 š“ | Move FeignClient call outside @Transactional in NotificationService |
Dev | YES |
| P1 š | Paginate ProductRepo.findAll() ā return Page<Product> |
Dev | YES |
| P1 š | Add @Retry + @CircuitBreaker to new PricingClient call |
Dev | YES |
| P2 š” | Add @Cacheable to PricingClient.getPrice() for stable prices |
Dev | NO |
| P2 š” | Add timeout config to FeignClient: connectTimeout=2s, readTimeout=5s |
Dev | NO |
7. Spock Regression Guards Required
The following Spock specs must be added to prevent NFR regressions:
| Test | Spec File | Purpose |
|---|---|---|
| Batch API call (not per-item) | OrderServiceSpec.groovy |
Verifies 1 * mockClient.fetchBatch(_) and 0 * mockClient.fetch(_) |
| Paginated query | ProductServiceSpec.groovy |
Verifies Pageable is passed to repo |
| Circuit breaker fallback | NotificationServiceSpec.groovy |
Verifies fallback method fires on exception |
8. Sign-Off
| Check | Status |
|---|---|
| All CRITICAL items resolved | ā |
| All HIGH blockers resolved | ā |
| Regression Spock specs added | ā |
| JaCoCo threshold still met after fixes | ā |
Re-run /nfr-check shows 0 blockers |
ā |
NFR Sign-Off: ā Approved for merge
---
### 3. Save the Report File
```bash
REPORT_FILE="nfr-impact-report-$(git branch --show-current | sed 's/\//-/g').md"
# Cascade writes the report to this file in the repo root
echo "Report saved: $REPORT_FILE"
4. Append NFR Summary to PR (if PR already exists)
PR_NUMBER=$(gh pr list --head $(git branch --show-current) --json number --jq '.[0].number')
if [ -n "$PR_NUMBER" ]; then
gh pr comment $PR_NUMBER --body "## NFR Impact Report
$(head -60 $REPORT_FILE)"
echo "NFR summary appended to PR #$PR_NUMBER"
fi
5. Final Gate Check
If any CRITICAL or HIGH blocker items exist in the report:
š« NFR blockers prevent merge. Resolve all P0/P1 items, re-run /nfr-check, then /nfr-report.
If clean:
ā
NFR check passed ā 0 blockers. Safe to proceed to /deploy-story.