Proof protocol

The stack changes. The safety protocol does not.

Grayhaven works across legacy frontend, .NET, Java, Rails, WCF, spreadsheet, and workflow systems. We do not start by choosing a replacement framework. We start by proving what the current system does.

Every engagement follows the same sequence, regardless of stack.

  1. 01
    Map the system.
    Inventory modules, routes, services, dependencies, workflows, integrations, and migration blockers.
  2. 02
    Capture behavior.
    Turn critical workflows, API contracts, side effects, reports, and edge cases into executable evidence.
  3. 03
    Install the harness.
    Create tests, snapshots, fixtures, or parity checks that prove the existing system before anything moves.
  4. 04
    Ship one bounded increment.
    Modernize one route, workflow, module, service, report, or integration surface at a time.
  5. 05
    Verify parity.
    Show what changed, what did not, how rollback works, and what can safely move next.
Public specimen 001

AngularJS 1.8.3 Preflight

The first public system we used to show the protocol end to end: system map, behavior map, harness starter, migration sequence, first proof increment, and executive memo. The point is not Angular. The point is the artifact chain.

Download the sample Preflight
23 pages·290 KB·Public source·No email required
01 · Map

Module inventory with dependency risk.

Every AngularJS 1.8.3 module enumerated, scored, and tagged with blockers before any change is planned.

ngcorehighblocker
ngRouteroutinghighblocker
ngAnimateanimationmed
ngResourcehttpmed
ngMessagesformsmed
ngSanitizesecuritylow
ngAriaa11ylow
ngCookiesstoragelow
03 · Harness

Captured behavior, ready for CI.

A Playwright test locks in the user-list state machine and API contract before the migration begins.

test("user list loads", async ({ page }) => {
  await page.route("/api/users", (r) =>
    r.fulfill({ json: USERS }));
  await page.goto("/users");
  await expect(page.getByRole("status"))
    .toHaveText("Loading");
  await expect(page.getByRole("list"))
    .toContainText(USERS[0].name);
});
05 · Verify

Output parity, byte-identical.

Snapshot diff between the AngularJS render and the React render under the same API response.

legacy{ users: 12, loading: false }
react{ users: 12, loading: false }
0 differences · 12 properties matched
Excerpt · pattern translation
user-list.component.js
angular.module('app').component('userList', {
  template: require('./user-list.html'),
  controller: function ($http) {
    var ctrl = this;
    ctrl.loading = true;
    ctrl.users = [];

    ctrl.$onInit = function () {
      $http.get('/api/users').then(function (res) {
        ctrl.users = res.data;
        ctrl.loading = false;
      });
    };
  },
});
UserList.tsx
import { useEffect, useState } from 'react';

export function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/users')
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  return <UserListView users={users} loading={loading} />;
}

AngularJS component to React function component. Same endpoint, same loading transition, same view contract. Behavior preserved either side — verified by the harness before the migration ships.

Start small.

Book a modernization diagnostic. Leave with a first boundary, a risk map, and a recommended next step.

We are deliberately slow to take on engagements. If we are not the right fit, we will say so on the first call.

Check Preflight fitSee pricing

Contact

Ask whether this system is worth a Preflight.

Show us the system your team cannot safely change. We’ll tell you whether a Preflight makes sense, what boundary we would inspect first, and whether a first verified increment is realistic with the access and domain knowledge available. If we’re not the right team, we’ll say so.

Location
United States
Phone
(405) 320-8212
Hours
Monday - Friday
8:00 AM - 7:00 PM
Press Enter to send