FormsRefresh

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.

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 in 71 values, none of them among the evaluator's findings:

  1. 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)
  2. ORDER_DATA saved with the clock time. Forms stores NULL; the target lets the column default apply. (4 values)
  3. 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)
  4. Line total computed while typing, not when the quantity is validated. (5 values)
  5. Adding a line blanks the total of the earlier lines. (3 values)
  6. Save shows no alerts. Forms shows FRM-40400, then the trigger's success message. (10 values)
  7. Status line shows 'No records...' from opening until save; Forms' message line is empty. (10 values)
  8. Focus after opening stays on the menu button; Forms puts it on CUSTOMER_ID. (1 values)
  9. 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.

Retail: coverage

ModuleTriggersProgram unitsCoveredUnreachableBlocked
MAIN_MENU70700
ORDERS1431700
CATEGORIES_FORM90900
CUSTOMERS_FORM90900
EMPLOYEES_FORM90900
PRODUCT_FORM90900
total5736000

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 unitBranchClassScenarios or reason
ORDERS/ORDERS.PUSH_BUTTON10/WHEN-BUTTON-PRESSEDcommit succeeded (FORM_SUCCESS TRUE)coveredorder-two-lines
ORDERS/ORDERS.PUSH_BUTTON10/WHEN-BUTTON-PRESSEDcommit failed (FORM_SUCCESS FALSE)coveredorders-add-new, orders-save-fails
ORDERS/ORDERS/ON-CHECK-DELETE-MASTERdetail rows exist, delete refusedcoveredorders-delete-refused
ORDERS/ORDERS/ON-CHECK-DELETE-MASTERno detail rows, delete goes aheadcoveredorders-delete-empty
ORDERS/ORDERS/ON-POPULATE-DETAILSmaster record NEW or INSERT (or ORDER_ID null) - no detail querycoveredorders-add-new
ORDERS/ORDERS/ON-POPULATE-DETAILSdetail queriedcoveredorders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved
ORDERS/ORDERS/ON-POPULATE-DETAILScursor moved by the query, returned to the start itemcoveredorders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved
ORDERS/ORDERS/ON-POPULATE-DETAILScursor still on the start item after the queryunreachableThe 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_DETAILSrelation not deferred - GO_BLOCK and EXECUTE_QUERYcoveredorders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved
ORDERS/unit:QUERY_MASTER_DETAILSrelation deferred - SET_BLOCK_PROPERTY non-coordinatedunreachableThe only relation (ORDERS_ORDER_ITEMS) is not deferred and nothing changes that at run time.
ORDERS/unit:QUERY_MASTER_DETAILSGO_BLOCK to the detail failed - exception handlerblockedNeeds 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_FAILUREprevious built-in succeededcoveredorders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved
ORDERS/unit:CHECK_PACKAGE_FAILUREprevious built-in failed - raise FORM_TRIGGER_FAILUREblockedCalled 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_DETAILSdetail block NEW - nothing to clearcoveredorders-add-new, orders-browse, orders-delete-empty, orders-delete-refused, orders-navigate-unsaved
ORDERS/unit:CLEAR_ALL_MASTER_DETAILSdetail block queried - CLEAR_BLOCK(NO_VALIDATE), cursor returnedcoveredorders-add-new, orders-browse, orders-delete-empty
ORDERS/unit:CLEAR_ALL_MASTER_DETAILSchanged block below the master found (First_Changed_Block_Below)coveredorders-navigate-unsaved
ORDERS/unit:CLEAR_ALL_MASTER_DETAILSchanges - CLEAR_BLOCK(ASK_COMMIT), user discards or saves, detail clearedcoveredorders-navigate-unsaved
ORDERS/unit:CLEAR_ALL_MASTER_DETAILSchanges - CLEAR_BLOCK(ASK_COMMIT), user cancels, FORM_TRIGGER_FAILURE, cursor returnedcoveredorders-navigate-unsaved
Database PL/SQL units
UnitTypeBlocksCoveredClass
TRG_CALC_ITEM_TOTALTRIGGER11covered

Report: retail-coverage.md.

HR: coverage

ModuleTriggersProgram unitsCoveredUnreachableBlocked
CONSOLE50500
EMPLOYEES1431700
LOGIN20200
total2132400

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 unitBranchClassScenarios or reason
LOGIN/LOGIN.LOGIN/WHEN-BUTTON-PRESSEDuser and password match - console openscoveredconsole-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-PRESSEDno matching user - ALERT_FAILUREcoveredlogin-fail
CONSOLE/CONSOLE.EMPLOYEES/WHEN-BUTTON-PRESSEDEMPLOYEES not open - OPEN_FORMcoveredemployees-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-PRESSEDEMPLOYEES already open - GO_FORMblockedNeeds 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-PRESSEDblock changed - commitcoveredemployees-change-department, employees-change-job, employees-create
EMPLOYEES/BUTTONS.SAVE_BUTTON/WHEN-BUTTON-PRESSEDnothing changed - no commitcoveredemployees-details
EMPLOYEES/BUTTONS.DELETE_BUTTON/WHEN-BUTTON-PRESSEDconfirmed - delete and commitcoveredemployees-delete
EMPLOYEES/BUTTONS.DELETE_BUTTON/WHEN-BUTTON-PRESSEDcancelled - nothing deletedcoveredemployees-delete-refused
EMPLOYEES/BUTTONS.CLEAR_BUTTON/WHEN-BUTTON-PRESSEDchanged, confirmed - changes discarded and requeriedcoveredemployees-clear
EMPLOYEES/BUTTONS.CLEAR_BUTTON/WHEN-BUTTON-PRESSEDchanged, cancelled - changes keptcoveredemployees-clear
EMPLOYEES/BUTTONS.CLEAR_BUTTON/WHEN-BUTTON-PRESSEDnothing changed - no questioncoveredemployees-clear
EMPLOYEES/EMPLOYEES/ON-CHECK-DELETE-MASTERjob history exists, delete refusedcoveredemployees-delete-refused
EMPLOYEES/EMPLOYEES/ON-CHECK-DELETE-MASTERno job history, delete goes aheadcoveredemployees-delete
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILSmaster record NEW or INSERT - no detail querycoveredemployees-clear, employees-create
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILSDEPARTMENT_ID set - department queriedcoveredemployees-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-DETAILSDEPARTMENT_ID null - no department querycoveredemployees-search
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILSMANAGER_ID set - manager queriedcoveredemployees-apex, employees-change-department, employees-change-job, employees-clear, employees-close, employees-delete, employees-delete-refused, employees-details, employees-search
EMPLOYEES/EMPLOYEES/ON-POPULATE-DETAILSMANAGER_ID null - no manager querycoveredemployees-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-DETAILSEMPLOYEE_ID set - history queriedcoveredemployees-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-DETAILSEMPLOYEE_ID null on a queried record - no history queryunreachableEMPLOYEE_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-DETAILScursor moved by the queries, returned to the start itemcoveredemployees-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-DETAILScursor still on the start item after the queriesunreachableEvery 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_DETAILSrelation not deferred - GO_BLOCK and EXECUTE_QUERYcoveredemployees-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_DETAILSrelation deferred - SET_BLOCK_PROPERTY non-coordinatedunreachableNo relation of EMPLOYEES is deferred and nothing changes that at run time.
EMPLOYEES/unit:QUERY_MASTER_DETAILSGO_BLOCK to the detail failed - exception handlerblockedNeeds 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_FAILUREprevious built-in succeededcoveredemployees-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_FAILUREprevious built-in failed - raise FORM_TRIGGER_FAILUREblockedCalled 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_DETAILSa detail block holds rows - cleared without validationcoveredemployees-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_DETAILSevery detail block empty - nothing clearedcoveredemployees-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_DETAILScursor moved by the clearing, returned to the start itemcoveredemployees-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_DETAILSchanged detail block below the master - ask to commitunreachableDEPARTMENT, MANAGER and HISTORY allow no insert, update or delete, so a detail block is never CHANGED.
EMPLOYEES/unit:CLEAR_ALL_MASTER_DETAILSa detail block not cleared - raise FORM_TRIGGER_FAILUREblockedNeeds 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
UnitTypeBlocksCoveredClass
COMPOSE_URLFUNCTION33covered
GENERATE_TOKENFUNCTION55covered
VALIDATE_TOKENFUNCTIONNone0unreachable
ADD_JOB_HISTORYPROCEDURE11covered
INSERT_LOGPROCEDURENone0unreachable
SECURE_DMLPROCEDURENone0unreachable
APEX_TRG_01TRIGGERNone0unreachable
REQUESTS_TRG_01TRIGGERNone0unreachable
SECURE_EMPLOYEESTRIGGERNone0unreachable
UPDATE_JOB_HISTORYTRIGGER44covered
USERS_TRG_01TRIGGERNone0unreachable

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).

Repeatability

Every golden record was recorded three times from the restored baseline, and the three were byte-identical for all 17 retail and all 17 HR scenarios: retail, HR. 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.

Retail: from first conversion to parity

RoundScenarios at parityDefect valuesDefect groupsTarget changeElapsed
0 (as converted)1 of 17403164-replay 950 s
117 of 17004 files, +414 / -205 linesrepair 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.

RoundScenarios cleanDefect valuesTarget changeElapsed
0 (as converted)4 of 1773 in 13 scenarios-replay 5 min 04 s
117 of 170 (52 repaired, 21 waived)3 files, +37 / -21 linesrepair 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).

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:

PhaseElapsed
Find and pin the application, check its licence9 min
Host setup: schema, APEX runtime, compile, Forms Services sections9 min
Application profile and name-locator checks on three modules10 min
17 scenarios, branch classification and generic harness adjustments1 h 32 min
First coverage loop with a passing exit check48 min

The full loop (recording pass, diagnostics replay, three runs) takes 169 s per scenario on average.

Limits of this evidence