Skip to content

Add modernization demo scaffold: React + Spring Boot replacing COBOL/CICS card management#170

Open
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1777010358-demo-scaffold
Open

Add modernization demo scaffold: React + Spring Boot replacing COBOL/CICS card management#170
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1777010358-demo-scaffold

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented Apr 24, 2026

Summary

Complete modernization demo: COBOL/CICS Card Management green screens → React + Spring Boot web application. Includes full demo scaffold (frontend, backend, BRE docs, presentation script) plus spec-driven BRE coverage improvements raising logic coverage from 68% → 93% (28/30 rules fully covered, 0 missing).

Key changes in latest iteration (BRE coverage fixes):

  • Name alpha-only validation (V-07) — regex ^[A-Za-z ]+$ matching COBOL INSPECT CONVERTING
  • Day=01 enforcement (V-09) — EXPDAY field DRK,PROT convention
  • Composite key verification (V-11) — /api/cards/{cardNumber}/verify?accountId= endpoint
  • Optimistic concurrency control (D-05/D-06) — @Version on Card entity + @Transactional
  • Account existence verification (D-07) — existsById() + FK constraints
  • CVV excluded from API responses — @JsonIgnore
  • GlobalExceptionHandler (@RestControllerAdvice) — structured error handling for validation (400), concurrency (409), and abend (500)
  • PF5 confirmation dialog in React before save (N-03)
  • Pagination with 7-row pages and PF7/PF8 navigation (N-04)
  • Informational messages in list page (E-02)
  • Account ID zero-padding on search
Metric v1 v2
Logic Coverage 68% (18/30) 93% (28/30)
Live Readiness 53% (9/17) 88% (15/17)
Rules Missing 7 0

Review & Testing Checklist for Human

  • Verify pagination works: Navigate to http://localhost:3000, confirm 7 rows per page, click PF8 Next to see remaining 3 cards on page 2, click PF7 Prev to go back
  • Test name validation: Edit a card, enter a name with numbers (e.g., "Test123") — should show "Name must contain only letters and spaces" error. Current regex is strict (no hyphens/apostrophes) — confirm this matches your business requirements
  • Test confirmation dialog: Click Save on edit page — modal should appear asking for confirmation before actual save
  • Verify CVV is hidden: Call GET /api/cards/0500024453765740 — response should NOT contain cvvCode field
  • Test day validation: Try saving with expiry date ending in anything other than "-01" — should reject with "Day must be 01" error

Notes

  • Run backend: cd demo/backend && mvn spring-boot:run (requires Java 17)
  • Run frontend: cd demo/frontend && npm install && npm start
  • The 2 partially-covered rules are S/U row selection (N-05, replaced with View/Edit links) and PF3 exit (N-06, replaced with breadcrumb navigation). Both are functional equivalents with better UX.
  • Auth/RBAC (N-08) documented as out-of-scope for demo; COMMAREA (N-07) reclassified as N/A (architectural difference).
  • Audit report at demo/business-rules/bre-audit-report.md

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/4b8ce2b840524f96a08a29f34912454b
Requested by: @VedantKh


Open in Devin Review

…CICS card management

- DEMO.md: CTO presentation script with architecture diagram and talking points
- demo/frontend/: React SPA with CardListPage, CardDetailPage, CardUpdatePage,
  and GreenScreenPreview (3270 terminal mockup) components
- demo/backend/: Spring Boot 3.x REST API with JPA entities (Card, Account,
  CardXref) mapped from COBOL copybooks, validation service with COBOL
  paragraph traceability, H2 database with seed data from mainframe files
- demo/business-rules/: BRE documentation mapping COBOL programs to Java,
  screen-to-component mappings, and data model crosswalk
- demo/README.md: Setup and run instructions
- README.md: Added Modernization Demo section with links

Co-Authored-By: Vedant Khanna <[email protected]>
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment thread demo/backend/src/main/java/com/carddemo/model/Card.java
Comment on lines +42 to +44
if (updated.getEmbossedName() != null && !updated.getEmbossedName().isBlank()) {
existing.setEmbossedName(updated.getEmbossedName().trim());
}
Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Backend silently ignores blank embossedName instead of rejecting it

In CardService.updateCard() at line 42, a blank or null embossedName silently preserves the old value instead of returning a validation error. This diverges from the original COBOL 1230-EDIT-NAME paragraph which rejects blank names with an error message. While the React frontend validates name-not-blank, a direct API caller (e.g., PUT /api/cards/0500024453765740 with {"embossedName": "", "activeStatus": "Y", "expirationDate": "2023-03-09"}) would get a 200 OK response with the old name preserved, instead of a 400 error. The activeStatus and expirationDate fields both properly reject invalid values, making this inconsistent.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Backend:
- Add name alpha-only validation (V-07): regex ^[A-Za-z ]+$ matching COBOL INSPECT CONVERTING
- Add day=01 enforcement (V-09): EXPDAY field DRK,PROT convention
- Add composite key verification (V-11): getCardByAccountAndCard() + /verify endpoint
- Add @Version optimistic locking (D-05/D-06): equivalent to 9300-CHECK-CHANGE-IN-REC
- Add @transactional on updateCard (D-05): equivalent to EXEC CICS READ UPDATE
- Add account existence check (D-07): existsById() + FK constraints in schema
- Add @JsonIgnore on cvvCode: prevent CVV exposure in API responses
- Add GlobalExceptionHandler (@ControllerAdvice): handles validation, concurrency, abend errors
- Add confirmation flag on update (N-03): equivalent to PF5 in 2000-DECIDE-ACTION
- Add pagination support (N-04): 7-row pages matching WS-MAX-SCREEN-LINES

Frontend:
- Add alpha-only name validation in CardUpdatePage
- Add day=01 validation with COBOL convention hint
- Add confirmation modal dialog before save (PF5 equivalent)
- Add pagination with PF7 Prev / PF8 Next buttons in CardListPage
- Add account ID zero-padding on search (PIC 9(11) behavior)
- Add informational messages (E-02): search results count, no-records-found

Schema:
- Add version column for optimistic locking
- Add foreign key constraints (cards→accounts, xref→cards/accounts)
- Add index on cards.account_id
- Update seed data: dates use day=01, accounts inserted before cards

Audit report regenerated: 28/30 rules covered, 2 partially, 0 missing.
Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +96 to +117
if (updated.getEmbossedName() != null && !updated.getEmbossedName().isBlank()) {
validateEmbossedName(updated.getEmbossedName());
existing.setEmbossedName(updated.getEmbossedName().trim());
}

// Equivalent to 1240-EDIT-CARDSTATUS in COCRDUPC.cbl
// Active status must be Y or N (FLG-YES-NO-VALID VALUES 'Y', 'N')
if (updated.getActiveStatus() != null) {
validateActiveStatus(updated.getActiveStatus());
existing.setActiveStatus(updated.getActiveStatus());
}

// Equivalent to 1250-EDIT-EXPIRY-MON + 1260-EDIT-EXPIRY-YEAR in COCRDUPC.cbl
if (updated.getExpirationDate() != null) {
validateExpirationDate(updated.getExpirationDate());
existing.setExpirationDate(updated.getExpirationDate());
}

// JPA @Version handles optimistic locking automatically on save().
// If another transaction modified the record, OptimisticLockException is thrown,
// equivalent to 9300-CHECK-CHANGE-IN-REC detecting field-level changes.
return cardRepository.save(existing);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Optimistic locking is ineffective: client-provided version is discarded during update

In CardService.updateCard(), the method reads the latest existing card from the database and copies only embossedName, activeStatus, and expirationDate from the client-submitted updated card — but never copies or checks the version field. The client does receive and send back the version (the Card.version field has no @JsonIgnore, so the frontend receives it in CardUpdatePage.jsx:149 via setForm(res.data) and sends it back in the PUT at line 208), but the backend discards it.

This means the @Version optimistic lock only guards against truly concurrent in-flight database transactions (a very narrow race window within the @Transactional), not against the intended scenario where User A loads a card, User B updates it, then User A saves stale data — overwriting User B's changes silently. This defeats the purpose of rule D-06 / 9300-CHECK-CHANGE-IN-REC from the COBOL equivalent.

Concrete scenario showing silent data loss
  1. User A loads card (version=0)
  2. User B loads card (version=0)
  3. User B saves → DB version becomes 1
  4. User A saves → findById reads version=1 from DB, copies A's form fields onto it, saves with version=1 → succeeds, version becomes 2
  5. User B's changes are silently overwritten
Prompt for agents
In CardService.updateCard(), the version field from the client-submitted Card (updated) is never compared against or applied to the existing entity before saving. This means JPA's @Version optimistic lock only protects against narrow in-flight transaction races, not the broader scenario where a user loaded data minutes ago and another user modified it in the meantime.

To fix this, after reading the existing card from the DB and before saving, set the version from the client onto the existing entity:

  existing.setVersion(updated.getVersion());

This should be done after all field copies and before cardRepository.save(existing). This way, JPA will issue UPDATE ... WHERE version = <client_version>, which will fail with OptimisticLockingFailureException if the record was modified since the user loaded it. The GlobalExceptionHandler at config/GlobalExceptionHandler.java:49-58 already handles this exception and returns a 409 Conflict response, and the frontend at CardUpdatePage.jsx:214-215 already handles 409 status codes with an appropriate error message.

Alternatively, you can explicitly check: if updated.getVersion() != null and !updated.getVersion().equals(existing.getVersion()), throw an appropriate exception immediately.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants