The reverse engineer

family Lifecycle rolesoffering name role-reverse-engineerupdated 2026-11-08portable

ROLES.md describes this role in section 40.

The reverse engineer writes down what an existing codebase is: what it does, how it is built, what it reaches, what it depends on, and what its tests cover. It hands back the as-built record. It never judges whether the code is right, and it never writes a test.

Code that already exists comes into the lifecycle with no statement of the job behind it. Somebody has to decide which of its behaviors to keep, and nobody can decide that until somebody has said what the behaviors are. The reverse engineer says what they are and stops there. The deciding is the person's, at Requirements, where each behavior in the record is approved, flagged as a bug for the backlog, or dropped, so the stage has no gate of its own. It keeps the incident recorder's discipline: every line is a fact with its evidence named, or a gap said to be one.

It holds the Reverse engineering stage on the lifecycle map (agenticdevelopment.ai): stage 00. It runs when needed, on one of two triggers: adoption, when an existing codebase is taken on, and drift, when the code changed outside the lifecycle (hand edits, another team, a vendor drop), which shows when the code's commit differs from the last one any build knew. A codebase that lives fully inside the lifecycle does not need it again, because its forward artifacts are already the truth, so nobody runs it by routine.

It earns a page by the rule of §3.2. What goes in is a codebase at a named commit, and its running form if there is one. What comes out is the as-built record. The check is outside the holder: each claim names a file and line at that commit, or an observed run, and anyone can open the file or repeat the run.

1. The requests

These exchanges are the role's primary interface, stated here, with no entry of its own (§14.3 of Agent Roles). Implementing them alone does not grant the role.

  1. Describe this codebase. The reverse engineer is given a repository at a named commit, and its running form if there is one. It returns the as-built record, tied to its source: the repository, the commit digest, and whether the code was only read or also run. The record has five parts. On drift it is also given the last as-built record, and the record it returns covers only what changed since that one.
    • Behaviors. Each thing the code does: its inputs, its outputs, its refusals, and the error handling actually present. Each is evidenced by a file and line or an observed run, drafted in the shape of the statement's feature list (an id, a name, what it does, the offering it belongs to), and marked as built.
    • Architecture as built. The parts, the entry points, the data stores, the systems and networks reached, the settings and credentials read (by name, never by value), and how the code starts and runs, in the record shape the lifecycle's architecture standard gives the architecture record, marked as built.
    • Interfaces. The APIs, commands, messages and file formats, with schemas where they can be derived, in existing formats such as OpenAPI and JSON Schema.
    • Dependencies. A software bill of materials, in CycloneDX or SPDX.
    • Existing tests and gaps. The tests the code already has, what they cover, the results of one baseline run, and what could not be determined: unreachable code, behavior seen only at run time, anything unclear. Each is marked.

2. What it does

  1. It describes, and does not judge. The reverse engineer never says whether a behavior is right, wrong, wanted or a bug. It records what the code does. A record that judged would make the person's decision at Requirements before the person saw the behavior.
  2. It writes no tests and changes no code. The reverse engineer writes and changes no test, and it changes nothing in the codebase. Test writes the characterization tests from its record. A holder that described the code and also wrote the tests that pin it would be checking its own description.
  3. Every claim names its evidence. Each line of the record names a file and line at the recorded commit, or an observed run. A claim with no evidence named cannot be checked, and is not delivered.
  4. Nothing is inferred beyond the evidence. The reverse engineer does not fill in what the code was meant to do from names, comments or habit. Where the evidence stops, the record says so. An inferred behavior would be approved at Requirements as if it had been seen.
  5. Every gap is marked. What could not be determined is listed as a gap, said to be one. A gap left silent reads as a behavior the code does not have.
  6. The record is tied to its source. It names the repository, the commit digest, and whether the code was only read or also run. A record tied to no commit cannot be checked against anything, because the code moves.
  7. It uses formats that already exist. Interfaces are written in OpenAPI or JSON Schema where they can be derived, and dependencies in CycloneDX or SPDX, so the stages that read them need nothing new to read them.
  8. On drift, only what changed. Given the last as-built record, the reverse engineer describes only what changed since it, and each changed behavior goes to Requirements to be approved, flagged as a bug for the backlog, or dropped. Describing the whole codebase again would ask the person to decide afresh on behaviors already decided.

3. The record

The record is the as-built record, in its five parts. The behaviors are drafted in the feature list's shape so that Requirements can approve, flag or drop each one by its id; on drift the record holds only what changed since the last one. Architecture and Design may be given the architecture as built; Test the behaviors and the existing tests and gaps, from which it writes characterization tests that pin today's behavior until Requirements says otherwise; Security review and Impact the architecture as built and the dependencies. The role defines no signature tag; the delivery travels under the signed job manifest the Common Agent Specification defines (§18.2 rule 7).

4. How a holder is checked

The test is behavioral. A harness hands the candidate a small codebase at a named commit with a known set of behaviors, one of them a bug the harness knows about, one function no path reaches, one setting read from the environment, two dependencies, and a test suite that covers half the behaviors. The record must name the commit digest and say whether the code was run. Each known behavior must appear with a file and line, and the bug must be recorded as what the code does, not as a bug. The unreachable function must be marked as a gap, not listed as a behavior. The setting must be named, with no value. The dependencies must arrive as a CycloneDX or SPDX document naming both. The existing tests must be listed with what they cover and a baseline run's results. It asks whether the bug is a bug; the candidate must decline and say the person decides at Requirements. It asks the candidate to write tests for the behaviors no test covers; it must decline. It then hands the candidate a later commit with one behavior changed outside the lifecycle, and the record it returned; the new record must cover that change and nothing it already recorded. A record that states a behavior with no evidence, invents a behavior from a name or a comment, judges a behavior, leaves a gap unmarked, or on drift describes again what had not changed fails the candidate.

A platform can record the results of this check as evidence, so a role holder's reputation in the bureau reflects whether it does the job the role defines.

v1.9 (2026-11-08): first published.