How we work
Two pieces of tooling and one rule. The tooling recovers what a system does and builds its replacement through gates. The rule is that a model may propose, but never decide.
Recovering the specification
Legacy modernization is almost never a lift-and-shift. We extract the as-was system, then your subject-matter experts change the requirements: keep some, rewrite some, drop some, add new. That decision, made for every behaviour in every application, is the project. Our tooling exists to make it explicit, reviewable and traceable instead of implicit and lost.
One graph for the whole portfolio
Source is parsed into syntax trees and call graphs with Tree-sitter, and compiled into a directed graph. Community detection groups the graph into candidate bounded contexts. Requirement nodes are written into the same graph from code, from documents, or from an existing requirement list, so every later step reads one structure.
Dependencies that refuse to fake precedence
Build order is only as good as the dependency edges, so they are produced in layers. Structural neighbours form a cheap, high-recall candidate set. A model adjudicates each candidate: is it a real prerequisite, in which direction, of what type, with what confidence and why. Every edge carries its provenance, asserted or inferred, and a confidence score. Edges that are merely thematic, "both about billing", group requirements but never order them.
A planner that calls no model
The iteration planner is deterministic and runs offline. It selects ordering edges, detects cycles and surfaces them for a person to break rather than silently linearizing, and lays out topological waves of work. Same inputs, same plan, every time, and the result is unit-testable.
Requirements as behaviour, not prose
Every requirement is written as a Gherkin scenario: given a situation, when something happens, then this is the outcome. The business rules behind them come from three places, in this order of trust: extracted from the legacy code with its line cited, extracted from the documents that exist, and drawn out in interviews with the people who run the process today. A scenario says where its rule came from. The same scenarios drive the prototype in Phase 1 and become the acceptance suite in Phase 2, so nothing is implemented that does not trace to one.
Non-functional targets, measured like behaviour
A system can pass every scenario and still be too slow, unreadable to a screen reader or impossible to restore. So Phase 1 also produces a sheet of non-functional targets: response time under load, accessibility (WCAG 2.1 AA by default), security controls, availability, backup and recovery, data residency, and official languages where they apply. Each target has a threshold and the automated check that proves it, for instance "95% of searches answer in under 800 ms with 200 users active, measured by a load test". You sign the targets with the scenarios. In Phase 2 a missed target is a defect, exactly like a failed scenario.
Three human gates, by design
- Requirement review: confirm, de-duplicate and disambiguate what was extracted, with its provenance.
- Dependency review: confirm inferred edges before the plan is final.
- Artifact review and legacy capture: review the specification, the acceptance scenarios and the non-functional targets, and snapshot golden records for the behaviours that stay unchanged.
The human step is not polish. In modernization it is the pivot where as-was becomes to-be. Phase 1 ends when that pivot is visible: a prototype on mocked data, generated from the same API contract the real system will implement, so what you approve on screen is what Phase 2 is measured against.
Building through gates
Implementation runs on a versioned template we maintain as an internal product: a full requirements-to-production lifecycle installed into every project, with 22 commands, 14 rule sets and a composable knowledge base so that application n+1 inherits every convention established by application n. Behaviour-driven development is the spine of it: the Gherkin scenarios are the source of truth, and step definitions against them are the tests that get sealed.
The chain is fixed: the requirements document passes a review gate before design starts; design documents are generated from an approved profile; acceptance scenarios are drafted from the requirements; the API contract is the single source of truth so code and mocks cannot drift; a test suite is sealed before implementation begins; the implementer makes those tests pass and never edits them; and a hardening step re-runs everything from a clean state and trusts no self-report.
How the fixed price is worked out
The quote is counted, not guessed. Our estimator reads what Phase 1 produced: the scenarios and their size, the API operations, screens, data entities, integrations, data to migrate, open unknowns and the non-functional targets. It prices each in person-days from unit efforts that are re-fitted to actual effort after every engagement, and adds three lines most quotes hide: support for your UAT at every milestone, a reserve for fixing the defects UAT will find, and a contingency sized from what is still uncertain. A model may propose the size of a scenario, citing its lines; the arithmetic runs with no model, and a named person signs the quote. Same inputs, same price.
The method measures itself
The most useful thing in the method's own changelog is the record of where its tooling was wrong. A structural-debt detector scored 22% precision on a real 2,239-symbol codebase; acting on it blindly would have deleted a hundred working components. The fix was a three-tier protocol: graph analysis with no model, then a mechanical text check with no model, then judgement, by a model, only on what survived. A dependency count that missed type-only imports was "a confidently wrong number, not a missing one", and now a verification pass counts real importers. We publish those misses internally because a method that distrusts its own output is the one an auditor can trust.
What the AI may and may not do
- Deterministic where it counts. Build order, dependency ordering and parity checking run with no model involved.
- Every extracted value is grounded. A proposed fact must cite the exact source construct it was read from, and that citation is verified mechanically before the fact is used. An unsupported value is rejected by the tooling, not believed.
- Fail closed. What cannot be verified is marked unknown and routed to a person. The system does not guess and does not quietly fill a gap.
- Humans hold the gates. Extracted requirements, dependency decisions and the keep-or-change call are signed off by your subject-matter experts before anything is built.
- Your code can stay inside your perimeter. Parsing, graph construction and planning run entirely offline. The stages that use a model can point at a self-hosted open-weight model in the jurisdiction you require: Canada, the United States, or your own data centre. We have run that configuration on our own infrastructure.
The full statement of stages, models, data flows and gates is on the AI-use page, and we provide it in writing for any engagement.