Evidence
The measured numbers behind the write-up, with the files they come from. All runs used Oracle Forms and Infrastructure 14.1.2 under Oracle's trial licence on one evaluation host, Oracle Database Free, and the Forms Standalone Launcher on a virtual display. The migrated apps ran on .NET 10 and PostgreSQL, and again on the original Oracle schemas in Oracle Database XE.
What the check caught
Every conversion of the demo apps was written by a coding agent, and every one was checked against recordings of the running original. What the check found that the agent had missed:
| Conversion | Caught | By cause | Repair |
|---|---|---|---|
| Retail: the agent's .NET/PostgreSQL build, once it had passed the independent database and API checks | 9 defects, 71 values, in the first scenario checked | order number not drawn on opening (11 values), order date saved with the clock time instead of NULL (4), line keys drawn by the app instead of typed (26), line total computed while typing (5), earlier totals blanked when a line is added (3), no alerts on save (10), a status text Forms does not show (10), focus after opening (1), Tab order (1) | 1 round |
| Retail: the same build with those 9 repaired, all 17 scenarios | 403 values in 164 groups, 12 causes | main menu start-up state, keys typed by the user drawn from sequences, record navigation and its messages, Add, Save and Delete semantics, date display, an exit alert, ORDERS opening on a query instead of a new numbered record, the three-button save question, the refused delete of an order with lines, the messages of a failed save, an added order that cannot be saved, the messages after a query | 1 round |
| HR: written from the Forms source alone | 73 values in 13 of 17 scenarios: 52 misses, 21 now waivers | 52 misses: the record counter (40 values), focus after alerts and tab clicks (6), the message line (4), the new employee id (2). 21 values the customer decides on: APEX links that fail in the original (13, waiver H1), a stale department number (8, waiver H2) | 1 round |
| CareWell: written from the Forms source alone | 244 values in 22 of 22 scenarios: 81 misses, 163 now waivers | 81 misses: every save of a new appointment or payment failed on the key's sequence draw (41 values), a fetched record refused by validation did not stop the query (14), a defaulted payment row after an empty detail query (8), Enter Query (7), POST-QUERY and the cursor after a delete or a failed save (6), validation from a list without a match (3), the record counter (2). 163 values the customer decides on: the collected-today gauge (114, waiver C2), Oracle's frame title before sign-in (46, C1), three single values (C3 to C5) | 1 round |
| Student forms: written from the Forms source alone | 1134 values in all 15 scenarios, 12 causes | rows in key order where the original returns storage order (941 values), dates shown DD-MON-YY instead of DD-MON-YYYY (120), windows Forms shows at start-up (32), the record counter (19), audit dates saved with the time of day (9), the sequence a new course number comes from (2), the message line not cleared when the cursor moves (2), and one value each for a record key, the focus after a failed save, a list-of-values message and the list's row order; 5 values are a screen-reader announcement, now waiver S1 | 2 rounds |
| Angular front ends of the four apps | retail, CareWell and student nothing; HR: the page rendered nothing | retail 17 of 17, CareWell 22 of 22 and student 15 of 15 on the first run. HR: the first run found the page empty; a zoneless Angular refresh redraws only views marked as changed, and the HR view state is plain data | HR: 1 fix |
| Oracle data layer of the four apps (the database kept) | retail and HR nothing: 17 of 17 each on the first run; CareWell: tied rows, a localised error; student: the row order | retail and HR none; 155 retail values tolerated on PostgreSQL (5000.00 for 5000) are exact matches on Oracle. CareWell: two patients of the same name came back in the other order (the query now ends with the key), and Oracle's localised error text hid the constraint name the library's message depends on. Student: in some runs the student list started at another student; the app read in rowid order, which a table's extents need not follow, and now reads with no ORDER BY, as the forms do | CareWell: 2 fixes; student: 1 fix |
| Restyle and navigation rewrites of both apps | nothing: diffs byte-identical | none: every diff byte-identical to the evidence before the change, so the check confirmed that nothing the user or the database sees had changed | none |
Three of the causes in plain words:
- Orders opened on a query instead of a new numbered record. The original form draws the next order number as it opens and is ready for a new order; the conversion showed existing orders.
- The record counter. Forms shows "1/?" until it has fetched the last record, in batches of the rows on screen; the conversion showed the total at once (40 values in HR).
- Line keys typed, not drawn. In the original the user types each order line's key; the conversion drew it from a sequence and made the item read-only. The stored keys agreed only because a fresh sequence starts at 1.
Why this matters at scale. These are small sample apps, retail with 6 forms, HR with 3, CareWell with 5 and the student forms with 3, and agents still missed behaviour like this. A large application maintained for many years carries far more undocumented behaviour, and nobody has a full list of it. That is where checking against the running original pays off: the recording is the list.
Migration trails
| Trail | Status | Evidence |
|---|---|---|
| Forms to .NET web on PostgreSQL | proven, live | every scenario of each app: 17 of 17 retail, 17 of 17 HR, 22 of 22 CareWell, 15 of 15 student |
| Oracle stays: Forms to .NET web on the original Oracle schema | proven, evidence only | 17 of 17 retail and 17 of 17 HR, three runs, 0 defects on the first run: parity, every difference from PostgreSQL explained; 22 of 22 CareWell, three runs (summary), after two fixes its runs found; 15 of 15 student, diffs byte-identical to PostgreSQL's (summary) |
| Angular front end on the same back end | proven, live | every scenario, diffs byte-identical to the default front end's: 17 of 17 retail (summary), 17 of 17 HR (summary), 22 of 22 CareWell (summary), 15 of 15 student (summary); HR needed one fix on its first run |
| Oracle stays, Forms becomes APEX | proven | all four apps rebuilt from native APEX components on their original Oracle schemas, every scenario at parity with 0 defects, differences from Forms proposed as waivers: APEX |
| React or Blazor front end | adapter plug point documented | a front end is a mapping variant; the steps to add one are written down |
| SQL Server | on request | the database is a plug point of the harness and of the targets' data layer; a third adapter is not built |
Rewrites that changed nothing
Both apps were later restyled, and their navigation rewritten (a visible call stack, Back as the way out of a called form). Three fresh-database runs of every scenario after each change gave diffs byte-identical to the evidence before it: the check confirmed that nothing the user or the database sees had changed. Files: restyle retail, HR; navigation retail, HR.
What database and API checks missed
An independent evaluator checked a .NET/PostgreSQL rebuild of the retail app, written by an ordinary coding agent, through its database and its API: it passed four of five finding groups (13 of 16 SQL observations, 7 of 10 API checks). The first recorded scenario (create an order with two lines, change a quantity, save) replayed against the same build found 9 defects, covering 71 values, none of them among the evaluator's findings:
- No order ID on open. Forms draws ORD_SEQ on opening, shows 16 and saves 17; the target shows nothing until save and saves 16. (11 values)
- ORDER_DATA saved with the clock time. Forms stores NULL; the target lets the column default apply. (4 values)
- ITEM_ID cannot be entered; the target draws it from item_seq. Stored values agree only because the fresh sequence starts at 1. (26 values)
- Line total computed while typing, not when the quantity is validated. (5 values)
- Adding a line blanks the total of the earlier lines. (3 values)
- Save shows no alerts. Forms shows FRM-40400, then the trigger's success message. (10 values)
- Status line shows 'No records...' from opening until save; Forms' message line is empty. (10 values)
- Focus after opening stays on the menu button; Forms puts it on CUSTOMER_ID. (1 values)
- Tab from QUANTITY goes to UNIT_PRICE; in Forms it goes to ITEM_ID. (1 values)
Further ORDERS scenarios found data-level faults in the same build: it overwrote an existing order instead of inserting a new one, saved an order Forms refuses (a required field empty), and updated a line where Forms rejects a duplicate key.
Which number is which. The 9 defects (71 values) are one scenario, order-two-lines, against the agent's first build. They were repaired first; the repaired build then became round 0 of the retail rounds, where all 17 scenarios found 403 defect values (164 groups) in the other 16. The 403 in the write-up is that round 0.
Retail: coverage
| Module | Triggers | Program units | Covered | Unreachable | Blocked |
|---|---|---|---|---|---|
| MAIN_MENU | 7 | 0 | 7 | 0 | 0 |
| ORDERS | 14 | 3 | 17 | 0 | 0 |
| CATEGORIES_FORM | 9 | 0 | 9 | 0 | 0 |
| CUSTOMERS_FORM | 9 | 0 | 9 | 0 | 0 |
| EMPLOYEES_FORM | 9 | 0 | 9 | 0 | 0 |
| PRODUCT_FORM | 9 | 0 | 9 | 0 | 0 |
| total | 57 | 3 | 60 | 0 | 0 |
Decision branches: 18, of which 14 covered, 2 unreachable (retirement candidates) and 2 blocked. Database PL/SQL: 1 unit(s) in the coverage report, 1 of 1 blocks covered. Exit check: PASS.
Every branch with its class
| Trigger or unit | Branch | Class | Scenarios or reason |
|---|---|---|---|
ORDERS/ORDERS.PUSH_BUTTON10/WHEN-BUTTON-PRESSED | commit succeeded (FORM_SUCCESS TRUE) | covered | order-two-lines |
ORDERS/ORDERS.PUSH_BUTTON10/WHEN-BUTTON-PRESSED | commit failed (FORM_SUCCESS FALSE) | covered | orders-add-new, orders-save-fails |
ORDERS/ORDERS/ON-CHECK-DELETE-MASTER | detail rows exist, delete refused | covered | orders-delete-refused |
ORDERS/ORDERS/ON-CHECK-DELETE-MASTER | no detail rows, delete goes ahead | covered | orders-delete-empty |
ORDERS/ORDERS/ON-POPULATE-DETAILS | master record NEW or INSERT (or ORDER_ID null) - no detail query | covered | orders-add-new |
ORDERS/ORDERS/ON-POPULATE-DETAILS | detail queried | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/ORDERS/ON-POPULATE-DETAILS | cursor moved by the query, returned to the start item | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/ORDERS/ON-POPULATE-DETAILS | cursor still on the start item after the query | unreachable | The relation is not deferred, so QUERY_MASTER_DETAILS always does GO_BLOCK('ORDER_ITEMS') and the cursor always leaves the master item it started on. |
ORDERS/unit:QUERY_MASTER_DETAILS | relation not deferred - GO_BLOCK and EXECUTE_QUERY | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/unit:QUERY_MASTER_DETAILS | relation deferred - SET_BLOCK_PROPERTY non-coordinated | unreachable | The only relation (ORDERS_ORDER_ITEMS) is not deferred and nothing changes that at run time. |
ORDERS/unit:QUERY_MASTER_DETAILS | GO_BLOCK to the detail failed - exception handler | blocked | Needs GO_BLOCK('ORDER_ITEMS') to fail during master coordination. The block has no PRE-BLOCK, WHEN-NEW-BLOCK-INSTANCE or record-level validation that can refuse entry, and the master record was validated before coordination began; no input found that makes it fail. FRD would show it as CHECK_PACKAGE_FAILURE with FORM_SUCCESS FALSE. |
ORDERS/unit:CHECK_PACKAGE_FAILURE | previous built-in succeeded | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/unit:CHECK_PACKAGE_FAILURE | previous built-in failed - raise FORM_TRIGGER_FAILURE | blocked | Called after GO_BLOCK and GO_ITEM in the relation code. Needs one of those navigations to fail, the same condition as the QUERY_MASTER_DETAILS exception branch; no input found. |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | detail block NEW - nothing to clear | covered | orders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | detail block queried - CLEAR_BLOCK(NO_VALIDATE), cursor returned | covered | orders-add-new, orders-browse, orders-delete-empty |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | changed block below the master found (First_Changed_Block_Below) | covered | orders-navigate-unsaved |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | changes - CLEAR_BLOCK(ASK_COMMIT), user discards or saves, detail cleared | covered | orders-navigate-unsaved |
ORDERS/unit:CLEAR_ALL_MASTER_DETAILS | changes - CLEAR_BLOCK(ASK_COMMIT), user cancels, FORM_TRIGGER_FAILURE, cursor returned | covered | orders-navigate-unsaved |
Database PL/SQL units
| Unit | Type | Blocks | Covered | Class |
|---|---|---|---|---|
| TRG_CALC_ITEM_TOTAL | TRIGGER | 1 | 1 | covered |
Report: retail-coverage.md.
HR: coverage
| Module | Triggers | Program units | Covered | Unreachable | Blocked |
|---|---|---|---|---|---|
| CONSOLE | 5 | 0 | 5 | 0 | 0 |
| EMPLOYEES | 14 | 3 | 17 | 0 | 0 |
| LOGIN | 2 | 0 | 2 | 0 | 0 |
| total | 21 | 3 | 24 | 0 | 0 |
Decision branches: 32, of which 24 covered, 4 unreachable (retirement candidates) and 4 blocked. Database PL/SQL: 11 unit(s) in the coverage report, 13 of 13 blocks covered. Exit check: PASS.
Every branch with its class
| Trigger or unit | Branch | Class | Scenarios or reason |
|---|---|---|---|
LOGIN/LOGIN.LOGIN/WHEN-BUTTON-PRESSED | user and password match - console opens | covered | console-apex, console-logout, employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search, login-ok |
LOGIN/LOGIN.LOGIN/WHEN-BUTTON-PRESSED | no matching user - ALERT_FAILURE | covered | login-fail |
CONSOLE/CONSOLE.EMPLOYEES/WHEN-BUTTON-PRESSED | EMPLOYEES not open - OPEN_FORM | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
CONSOLE/CONSOLE.EMPLOYEES/WHEN-BUTTON-PRESSED | EMPLOYEES already open - GO_FORM | blocked | Needs the CONSOLE button pressed while EMPLOYEES is open. OPEN_FORM(ACTIVATE) puts the EMPLOYEES window over the console, and the way back is the FSAL "Window" menu, which is a client menu with no Forms name (LWMenu1); the runner only clicks named elements. Harness limit. |
EMPLOYEES/BUTTONS.SAVE_BUTTON/WHEN-BUTTON-PRESSED | block changed - commit | covered | employees-change-department, employees-change-job, employees-create |
EMPLOYEES/BUTTONS.SAVE_BUTTON/WHEN-BUTTON-PRESSED | nothing changed - no commit | covered | employees-details |
EMPLOYEES/BUTTONS.DELETE_BUTTON/WHEN-BUTTON-PRESSED | confirmed - delete and commit | covered | employees-delete |
EMPLOYEES/BUTTONS.DELETE_BUTTON/WHEN-BUTTON-PRESSED | cancelled - nothing deleted | covered | employees-delete-refused |
EMPLOYEES/BUTTONS.CLEAR_BUTTON/WHEN-BUTTON-PRESSED | changed, confirmed - changes discarded and requeried | covered | employees-clear |
EMPLOYEES/BUTTONS.CLEAR_BUTTON/WHEN-BUTTON-PRESSED | changed, cancelled - changes kept | covered | employees-clear |
EMPLOYEES/BUTTONS.CLEAR_BUTTON/WHEN-BUTTON-PRESSED | nothing changed - no question | covered | employees-clear |
EMPLOYEES/EMPLOYEES/ON-CHECK-DELETE-MASTER | job history exists, delete refused | covered | employees-delete-refused |
EMPLOYEES/EMPLOYEES/ON-CHECK-DELETE-MASTER | no job history, delete goes ahead | covered | employees-delete |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | master record NEW or INSERT - no detail query | covered | employees-clear, employees-create |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | DEPARTMENT_ID set - department queried | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | DEPARTMENT_ID null - no department query | covered | employees-search |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | MANAGER_ID set - manager queried | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-delete, employees-delete-refused, employees-details, employees-search |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | MANAGER_ID null - no manager query | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | EMPLOYEE_ID set - history queried | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | EMPLOYEE_ID null on a queried record - no history query | unreachable | EMPLOYEE_ID is the primary key of EMPLOYEES, so a queried record always has it; NEW and INSERT records return before this point. |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | cursor moved by the queries, returned to the start item | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILS | cursor still on the start item after the queries | unreachable | Every relation is not deferred, so each detail query does GO_BLOCK to the detail and the cursor always leaves the master item; HISTORY is always queried (EMPLOYEE_ID is the key). |
EMPLOYEES/unit:QUERY_MASTER_DETAILS | relation not deferred - GO_BLOCK and EXECUTE_QUERY | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/unit:QUERY_MASTER_DETAILS | relation deferred - SET_BLOCK_PROPERTY non-coordinated | unreachable | No relation of EMPLOYEES is deferred and nothing changes that at run time. |
EMPLOYEES/unit:QUERY_MASTER_DETAILS | GO_BLOCK to the detail failed - exception handler | blocked | Needs GO_BLOCK to a detail block to fail during coordination. The detail blocks have no PRE-BLOCK, WHEN-NEW-BLOCK-INSTANCE or validation that can refuse entry; no input found that makes it fail. |
EMPLOYEES/unit:CHECK_PACKAGE_FAILURE | previous built-in succeeded | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/unit:CHECK_PACKAGE_FAILURE | previous built-in failed - raise FORM_TRIGGER_FAILURE | blocked | Called after GO_BLOCK and GO_ITEM in the coordination units; no input found that makes these fail (see the QUERY_MASTER_DETAILS exception branch). |
EMPLOYEES/unit:CLEAR_ALL_MASTER_DETAILS | a detail block holds rows - cleared without validation | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-search |
EMPLOYEES/unit:CLEAR_ALL_MASTER_DETAILS | every detail block empty - nothing cleared | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-open, employees-search |
EMPLOYEES/unit:CLEAR_ALL_MASTER_DETAILS | cursor moved by the clearing, returned to the start item | covered | employees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-create, employees-delete, employees-delete-refused, employees-details, employees-search |
EMPLOYEES/unit:CLEAR_ALL_MASTER_DETAILS | changed detail block below the master - ask to commit | unreachable | DEPARTMENT, MANAGER and HISTORY allow no insert, update or delete, so a detail block is never CHANGED. |
EMPLOYEES/unit:CLEAR_ALL_MASTER_DETAILS | a detail block not cleared - raise FORM_TRIGGER_FAILURE | blocked | Needs CLEAR_BLOCK(NO_VALIDATE) on a read-only detail block to leave rows behind; no input found that makes it fail. |
Database PL/SQL units
| Unit | Type | Blocks | Covered | Class |
|---|---|---|---|---|
| COMPOSE_URL | FUNCTION | 3 | 3 | covered |
| GENERATE_TOKEN | FUNCTION | 5 | 5 | covered |
| VALIDATE_TOKEN | FUNCTION | None | 0 | unreachable |
| ADD_JOB_HISTORY | PROCEDURE | 1 | 1 | covered |
| INSERT_LOG | PROCEDURE | None | 0 | unreachable |
| SECURE_DML | PROCEDURE | None | 0 | unreachable |
| APEX_TRG_01 | TRIGGER | None | 0 | unreachable |
| REQUESTS_TRG_01 | TRIGGER | None | 0 | unreachable |
| SECURE_EMPLOYEES | TRIGGER | None | 0 | unreachable |
| UPDATE_JOB_HISTORY | TRIGGER | 4 | 4 | covered |
| USERS_TRG_01 | TRIGGER | None | 0 | unreachable |
Report: hr-coverage.md. Of the schema's 11 PL/SQL units, the four the Forms app runs are fully covered (13 of 13 blocks); the other seven cannot run from Forms (a policy function that ships disabled, its helper, the APEX side and audit triggers on tables the app never writes).
CareWell: coverage
| Module | Triggers | Program units | Covered | Unreachable | Blocked |
|---|---|---|---|---|---|
| CW_APPOINTMENTS | 9 | 3 | 12 | 0 | 0 |
| CW_BILLING | 10 | 4 | 13 | 1 | 0 |
| CW_LOGIN | 1 | 0 | 1 | 0 | 0 |
| CW_MAIN | 5 | 2 | 7 | 0 | 0 |
| CW_PATIENTS | 5 | 1 | 5 | 1 | 0 |
| CW_APP | 12 | 0 | 12 | 0 | 0 |
| CW_LIB | 13 | 0 | 8 | 5 | 0 |
| total | 55 | 10 | 58 | 7 | 0 |
Decision branches: 30, of which 24 covered, 5 unreachable (retirement candidates) and 1 blocked. Database PL/SQL: 2 unit(s) in the coverage report, 0 of 0 blocks covered. Exit check: PASS.
Every branch with its class
| Trigger or unit | Branch | Class | Scenarios or reason |
|---|---|---|---|
CW_LIB/unit:CW_SEC.ALLOW | role may not use the item - disabled | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-no-patient, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved, login-billing, login-lov, navigation, patients-find, patients-query |
CW_LIB/unit:CW_SEC.ALLOW | role may use the item - left enabled | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-no-patient, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved, login-billing, login-lov, navigation, patients-find, patients-query |
CW_LIB/unit:CW_SEC.LOGIN | known user - signed in | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-no-patient, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved, login-billing, login-lov, login-ok, main-about, main-print-schedule, navigation, patients-find, patients-query |
CW_LIB/unit:CW_SEC.LOGIN | unknown or inactive user - stop alert | unreachable | see CW_LIB/unit:CW_MSG.FAIL |
CW_LIB/unit:CW_NAV.OPEN_MODULE | form not open - OPEN_FORM | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-delete-refused, billing-patient, billing-pay, billing-unsaved, login-billing, login-lov, navigation, patients-find, patients-query |
CW_LIB/unit:CW_NAV.OPEN_MODULE | form open - GO_FORM | covered | navigation |
CW_LIB/unit:CW_ERR.ON_ERROR | check constraint APPOINTMENTS_DUR_CK - translated | covered | appointments-invalid-duration |
CW_LIB/unit:CW_ERR.ON_ERROR | child records (ORA-02292) - translated | covered | appointments-delete-refused |
CW_LIB/unit:CW_ERR.ON_ERROR | other error - Forms' own message | covered | billing-delete-refused |
CW_LIB/unit:CW_ERR.ON_ERROR | duplicate key (ORA-00001) - translated | unreachable | Every key the forms insert comes from a sequence in PRE-INSERT (APPOINTMENTS_SEQ, PAYMENTS_SEQ). |
CW_LIB/unit:CW_ERR.ON_ERROR | NOT NULL column (ORA-01400) - translated | unreachable | Every NOT NULL column the forms insert is a Required item (FRM-40202 first), has an initial value or is set in PRE-INSERT. |
CW_LIB/unit:CW_ERR.ON_ERROR | APPOINTMENTS_PATIENT_FK or APPOINTMENTS_DOCTOR_FK - translated | unreachable | PATIENT_ID comes from CTL.PATIENT_ID (a queried patient) and DOCTOR_ID from LOV_DOCTORS; neither is typed, and the forms delete no patient or doctor. |
CW_APPOINTMENTS/unit:CHECK_DOUBLE_BOOKING | overlap - refused | covered | appointments-double-booking, navigation |
CW_APPOINTMENTS/unit:CHECK_DOUBLE_BOOKING | no overlap or cancelled - accepted | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-patient, navigation |
CW_APPOINTMENTS/unit:LOAD_PATIENT | no patient chosen - message | covered | appointments-no-patient |
CW_APPOINTMENTS/unit:LOAD_PATIENT | another patient - query | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-patient, navigation |
CW_APPOINTMENTS/unit:LOAD_PATIENT | same patient - nothing | covered | appointments-return |
CW_BILLING/unit:LOAD_INVOICES | no patient - invoices to be paid | covered | billing-delete-refused, billing-pay, billing-return, billing-unsaved |
CW_BILLING/unit:LOAD_INVOICES | already showing - nothing | covered | billing-return |
CW_BILLING/unit:LOAD_INVOICES | a patient - that patient's invoices | covered | billing-patient |
CW_BILLING/KEY-COMMIT | saved - requery and message | covered | billing-pay |
CW_BILLING/KEY-COMMIT | not saved - no message | covered | billing-pay |
CW_MAIN/unit:PRINT_SCHEDULE | report finished | blocked | Needs Oracle Reports: the PoC host runs Forms without a Reports server, so RUN_REPORT_OBJECT fails (FRM-41214) and the job never finishes. Missing resource, not a harness limit. |
CW_MAIN/unit:PRINT_SCHEDULE | report not finished | covered | main-print-schedule |
CW_BILLING/unit:QUERY_MASTER_DETAILS | immediate relation - detail queried | covered | billing-delete-refused, billing-patient, billing-pay, billing-return, billing-unsaved |
CW_BILLING/unit:QUERY_MASTER_DETAILS | deferred relation | unreachable | Both relations of INVOICES (INVOICES_LINES, INVOICES_PAYMENTS) have Deferred No. |
CW_BILLING/unit:CLEAR_ALL_MASTER_DETAILS | unchanged details - CLEAR_BLOCK(NO_VALIDATE) | covered | billing-delete-refused, billing-pay, billing-unsaved |
CW_BILLING/unit:CLEAR_ALL_MASTER_DETAILS | changed details - CLEAR_BLOCK(ASK_COMMIT) | covered | billing-unsaved |
CW_PATIENTS/unit:FIND_PATIENTS | no search fields - every patient | covered | appointments-book, appointments-cancel, appointments-delete-refused, appointments-department, appointments-double-booking, appointments-invalid-duration, appointments-return, billing-patient, login-billing, login-lov, navigation, patients-find, patients-query |
CW_PATIENTS/unit:FIND_PATIENTS | search fields filled - filtered | covered | patients-find |
Database PL/SQL units
| Unit | Type | Blocks | Covered | Class |
|---|---|---|---|---|
| CW_API | PACKAGE BODY | None | 0 | unreachable |
| CW_FEE_API | PACKAGE BODY | None | 0 | unreachable |
Report: carewell-coverage.md. The menu module's items and the library's units are counted with the forms' triggers.
Student: coverage
| Module | Triggers | Program units | Covered | Unreachable | Blocked |
|---|---|---|---|---|---|
| QAFE_FORM1 | 3 | 0 | 3 | 0 | 0 |
| QAFE_FORM2 | 11 | 3 | 13 | 1 | 0 |
| QAFE_FORM3 | 8 | 1 | 9 | 0 | 0 |
| total | 22 | 4 | 25 | 1 | 0 |
Decision branches: 14, of which 10 covered, 4 unreachable (retirement candidates) and 0 blocked. Database PL/SQL: 10 unit(s) in the coverage report, 2 of 3 blocks covered. Exit check: PASS.
Every branch with its class
| Trigger or unit | Branch | Class | Scenarios or reason |
|---|---|---|---|
QAFE_FORM2/COURSE/ON-POPULATE-DETAILS | queried course - sections queried | covered | form2-course-delete-refused, form2-courses |
QAFE_FORM2/COURSE/ON-POPULATE-DETAILS | new course record - nothing | unreachable | FORM2's COURSE block has Insert Allowed No, so it never holds a NEW or INSERT record. |
QAFE_FORM2/unit:QUERY_MASTER_DETAILS | immediate relation - detail queried | covered | form2-course-delete-refused, form2-courses |
QAFE_FORM2/unit:QUERY_MASTER_DETAILS | deferred relation - detail marked non-coordinated | unreachable | COURSE_SECTION, FORM2's only relation, has Deferred No. |
QAFE_FORM2/unit:CLEAR_ALL_MASTER_DETAILS | unchanged details - CLEAR_BLOCK(NO_VALIDATE) | covered | form2-courses |
QAFE_FORM2/unit:CLEAR_ALL_MASTER_DETAILS | changed detail - CLEAR_BLOCK(ASK_COMMIT) | unreachable | The detail block SECTION has Insert, Update and Delete Allowed No (typing gives FRM-40200), so it is never CHANGED. |
QAFE_FORM2/unit:CHECK_PACKAGE_FAILURE | previous built-in succeeded | covered | form2-course-delete-refused, form2-courses |
QAFE_FORM2/unit:CHECK_PACKAGE_FAILURE | previous built-in failed - raise | unreachable | It follows GO_BLOCK and GO_ITEM to SECTION or back to the starting item; FORM2 has no navigation or validation trigger that could make them fail, and SECTION is never changed. |
QAFE_FORM3/COURSE.COST/WHEN-VALIDATE-ITEM | cost below 5 - refused | covered | form3-cost-invalid |
QAFE_FORM3/COURSE.COST/WHEN-VALIDATE-ITEM | cost 5 or more - accepted | covered | form3-cost-invalid |
QAFE_FORM3/SECTION.COURSE_NO/WHEN-VALIDATE-ITEM | unknown course - refused | covered | form3-section-course |
QAFE_FORM3/SECTION.COURSE_NO/WHEN-VALIDATE-ITEM | existing course - accepted | covered | form3-section-course |
QAFE_FORM3/COURSE.HIDESTUDENTCOUNT/WHEN-BUTTON-PRESSED | visible - hidden | covered | form3-browse |
QAFE_FORM3/COURSE.HIDESTUDENTCOUNT/WHEN-BUTTON-PRESSED | hidden - shown | covered | form3-browse |
Database PL/SQL units
| Unit | Type | Blocks | Covered | Class |
|---|---|---|---|---|
| ADF_EX10_01_TRIGGERS | PACKAGE BODY | None | 0 | unreachable |
| STUDENT | PACKAGE BODY | None | 0 | unreachable |
| STUDENTPKG | PACKAGE BODY | None | 0 | unreachable |
| TRIGGER_STUDENT_BEFORE_INSERT | TRIGGER | None | 0 | unreachable |
| TRIGGER_STUDENT_BEFORE_UPDATE | TRIGGER | 3 | 2 | partly covered |
| V_EX01_01_STUDENT_TRG | TRIGGER | None | 0 | unreachable |
| V_EX10_01_STUDENT_TRG | TRIGGER | None | 0 | unreachable |
| V_QAFE_FORM1_COURSE_TRG | TRIGGER | None | 0 | unreachable |
| V_QAFE_FORM2_INSTRUCTOR_TRG | TRIGGER | None | 0 | unreachable |
| V_QAFE_FORM2_STUDENT_TRG | TRIGGER | None | 0 | unreachable |
Report: student-coverage.md.
Repeatability
Every golden record was recorded three times from the restored baseline, and the three were byte-identical for all 17 retail, all 17 HR, all 22 CareWell and all 15 student scenarios: retail, HR, CareWell, student. Clock and run-date values are masked; the masks were checked by recording on another day (17 of 17 retail records equal) and with the Forms runtime's clock moved ten days ahead (one scenario per app). Every replay against a migrated app was also run three times from a fresh database, with byte-identical diffs: retail, HR, CareWell, student.
Retail: from first conversion to parity
| Round | Scenarios at parity | Defect values | Defect groups | Target change | Elapsed |
|---|---|---|---|---|---|
| 0 (first build, order-two-lines repaired) | 1 of 17 | 403 | 164 | - | replay 950 s |
| 1 | 17 of 17 | 0 | 0 | 4 files, +414 / -205 lines | repair 19 min, replay 896 s |
The 164 defect groups came down to twelve findings, among them: the main menu's start-up state, keys typed by the user instead of drawn from sequences, record navigation and its messages, Add, Save and Delete semantics, date display, ORDERS opening on a new record with a drawn order number, the three-button save question, the refused delete of an order with lines, and the messages of a failed save. All were repaired in one round by an agent that had the diffs and golden records but not the Forms source. Files: round 0, round 1.
HR: from first conversion to parity
The HR app was converted to .NET 10 and PostgreSQL from its Forms source by one coding agent in about 20 minutes (12 files, 1809 lines), without access to the scenarios or golden records.
| Round | Scenarios clean | Defect values | Target change | Elapsed |
|---|---|---|---|---|
| 0 (as converted) | 4 of 17 | 73 in 13 scenarios | - | replay 5 min 04 s |
| 1 | 17 of 17 | 0 (52 repaired, 21 waived) | 3 files, +37 / -21 lines | repair about 4 min, replay 5 min 18 s |
Round 0 defects by kind: the record indicator (40 values), focus after alerts and tab clicks (6), the message line (4), the generated employee id (2), the APEX links (13, waiver H1) and a stale department number (8, waiver H2). Files: round 0 summary, round 0 defects, round 1 summary, round 1 repair diff, final three runs. The APEX token's signing key was later moved from the source's literal to configuration; the two scenarios that use it give byte-identical diffs (re-run).
CareWell: from first conversion to parity
CareWell was converted to .NET 10 and PostgreSQL from its Forms source (the XML exports of five forms, the menu module and the library, and the schema and seed SQL) by one coding agent in about 35 minutes (9 files, 2690 lines besides the seed data), without access to the scenarios or golden records.
| Round | Scenarios clean | Defect values | Target change | Elapsed |
|---|---|---|---|---|
| 0 (as converted) | 0 of 22 | 244 in 22 scenarios | - | replay 35 min |
| 1 | 22 of 22 | 0 (81 repaired, 163 waived) | 5 files, +69 / -32 lines | repair about 30 min, replay 32 min |
Round 0 by cause: the key's sequence draw, which made every save of a new appointment or payment fail (41 values); a record refused by validation as it is fetched, which stops the query in Forms (14); a payment row defaulted after an empty detail query (8); Enter Query (7); POST-QUERY and the cursor after a delete or a failed save (6); validation from a list without a match (3); the record counter (2); and 163 values of the five waivers (the collected-today gauge 114, the frame title before sign-in 46, three single values). The Oracle run later found two more: two patients of the same name came back in the other order, because the conversion's query left them tied (fixed by ordering on the key, as the original returns them), and the database's localised error text hid the constraint name the library's error message depends on. A run after midnight found a third: a new payment took its date from the browser, a day ahead of the database clock (now from the database, as Forms' server clock). Files: round 0 summary, round 0 causes, round 1 summary, round 1 repair diff, final three runs.
Student: from first conversion to parity
The three student forms were converted to .NET 10 and PostgreSQL from their Forms source by one coding agent in about 39 minutes, without access to the scenarios or golden records. The same agent then wrote the mapping and the fixture and repaired from the diffs.
| Round | Scenarios clean | Defect values | Change | Replay |
|---|---|---|---|---|
| 0 (as converted) | 0 of 15 | 1134 | - | 17 min |
| 1 | 7 of 15 | 662 | app.js 54 lines, Program.cs 18, schema.sql 8, the fixture reordered by its generator | 14 min |
| 2 | 14 of 15 | 1 | app.js 4 lines, the fixture's STUDENT rows rotated to the original's storage order | 14 min |
| 3 (final) | 15 of 15 | 0 (5 waived, S1) | the harness: a generated value in a view-based block gets its label from the source database | 16 min |
Round 0 by cause: rows in key order where Forms shows storage order (941 values), the date format (120), the windows Forms shows at start-up (32), the record counter (19), audit dates with the time of day (9), a screen-reader announcement (5, waiver S1), the new course number's sequence (2), the message line (2) and four single values. Round 1 left the STUDENT table's storage order (the original's starts at student 385, 651 values) and a regression in the save (11). Files: round 0 summary and causes; round 1 summary, causes and repair; round 2 summary, causes and repair; final three runs.
Onboarding a new application
The HR app was the first application the harness had not been built on. One agent took 2 h 39 min from receiving the source to a passing coverage report:
| Phase | Elapsed |
|---|---|
| Find and pin the application, check its licence | 9 min |
| Host setup: schema, APEX runtime, compile, Forms Services sections | 9 min |
| Application profile and name-locator checks on three modules | 10 min |
| 17 scenarios, branch classification and generic harness adjustments | 1 h 32 min |
| First coverage loop with a passing exit check | 48 min |
The full loop (recording pass, diagnostics replay, three runs) takes 169 s per scenario on average.
Limits of this evidence
- Only the Forms Standalone Launcher on Forms 14.1.2 was tested; Forms 12.2.1.x, the Java plugin and Web Start are untested.
- The applications are small public samples (6, 3, 5 and 3 modules; 60, 24, 65 and 26 triggers and units).
- Parity covers the recorded scenarios of each app (17, 17, 22 and 15) and the items the mapping names. Behaviour no scenario exercises is unverified.
- Target adapters exist for a web app in a browser on PostgreSQL or Oracle (see migration trails). Files, reports, mail and HTTP calls are not recorded yet.
- Tolerance policies and waivers were drafted by the agent; agreeing them with a real customer is untested.