Skip to content
← Back to blog

Building self-healing Playwright with JS Proxy

How Autowright detects, diagnoses, and fixes automation failures

Autowright is a self-healing wrapper around Playwright. When a test or a scraper breaks — because a selector changed, a page restructured, or a new captcha appeared — it captures a full forensic bundle, ships it to an AI fixer running on Claude Agent SDK, and the fixer opens a pull request with a proposed fix.

This isn’t magic. It works because of a small architectural decision: wrap every Playwright object in a JS Proxy.

Why Proxy, not patches

The naive approach to adding observability to Playwright is to wrap every method with a higher-order function. That works, but it’s fragile — you miss chained calls, you miss this bindings, and every time Playwright adds a new method you have to remember to wrap it.

A Proxy handler sidesteps all of that. You intercept get, and every property access flows through your instrumentation automatically.

function wrap<T extends object>(target: T, onError: ErrorHook): T {
  return new Proxy(target, {
    get(obj, prop, receiver) {
      const value = Reflect.get(obj, prop, receiver);
      if (typeof value !== 'function') return value;
      return async (...args: unknown[]) => {
        try {
          return await value.apply(obj, args);
        } catch (err) {
          await onError(err as Error, { prop: String(prop), args });
          throw err;
        }
      };
    },
  });
}

This gives you a single choke point for capturing DOM snapshots, screenshots, network logs, console output, and the full action trace — the inputs the AI fixer needs to reason about what happened.

Full post coming soon. Future sections: the 12 error categories, the 24 validated recovery scenarios, and how the fixer’s PR hit-rate improved once we started feeding it the last 5 successful runs as context.