REFERENCE RUN · VERIFICATION REPORT
The verification report: 745 files, 87/87 routes, 11 flags
One production AngularJS ERP, migrated and verified end to end in 24 hours elapsed. This page walks through the evidence, including the part most vendors leave out.
What was migrated, exactly?
The reference run migrated 745 files of a production AngularJS 1.x ERP — Skewbird’s FreeSpace ERP. The inventory: 412 controllers, 138 directives, 96 services, 301 templates. Elapsed time: 24 hours. Result: 100% of emitted files compile under strict TypeScript, 87 of 87 routes render-verified in headless Chrome, and 11 files flagged for human review.
This was not a demo app or a trimmed sample. It is a live ERP with the usual accumulation: jQuery plugins, dynamic $compile usage, run-blocks doing access control, and dynamic $injector lookups that no static tool can resolve on its own. Those are exactly the parts the report is honest about below.
The run went through the full pipeline: a declared sequence of 65 steps across 8 lanes. Migration order was not hand-picked. It was computed from the dependency graph, with Tarjan strongly-connected-component condensation so that cyclic clusters are co-scheduled, unresolved edges are surfaced instead of ignored, and the order is deterministic — the same input produces the same waves every time.
| Metric | Value | Note |
|---|---|---|
| Source files | 745 | Production AngularJS 1.x ERP |
| Controllers | 412 | Converted to components |
| Directives | 138 | Converted to components and directives |
| Services | 96 | Converted to injectable services |
| Templates | 301 | Converted to Angular template syntax |
| Routes render-verified | 87 / 87 | Every route, not a sample |
| Strict TypeScript compile rate | 100% | All emitted files, strict mode on |
| Files flagged to issue registry | 11 | Each with file, reason, suggested owner |
| Elapsed time | 24 hours | Pipeline run, wall clock |
How is a migrated route actually verified?
Every route — all 87, not a sample — is loaded in headless Chrome, driven over the Chrome DevTools Protocol. A route passes only if it renders, the console shows zero errors, and its network calls resolve. This is a render bar, applied to the entire route table. Deeper data-parity comparison against the legacy app runs separately, during hardening.
The bar has three conditions, and all three must hold per route. First, the route renders. Second, zero console errors — a single console error fails the route, whatever caused it. Third, the network calls the route fires actually resolve. A page that paints while its API calls fail is not a migrated page, so “it loads” is not enough to pass.
“Every route” is worth dwelling on. 87/87 means the route table was enumerated and each entry driven individually. Sampling 10 routes and extrapolating would have been faster to report. It would also mean the number tells you nothing about the other 77.
The render bar is deliberately distinct from data parity, and the report says so rather than blurring the two. During hardening, a runtime parity harness drives the real legacy app and the migrated app side by side in dual headless Chrome sessions and compares behavior. That harness runs behind a mutation firewall: write verbs are blocked, so a sweep against a production system cannot write to it.
The parity harness also carries an honesty rule. A timeout or a backend error is recorded as NOT MEASURED. It is never counted as a pass, and never counted as a fail. Inflating a pass rate with lucky timeouts — or deflating it with backend flakiness — would make the number worthless, so the harness refuses to score what it did not actually observe.
What does “100% compiling” mean under strict TypeScript?
It means every emitted file passes the TypeScript compiler with strict mode on — no excluded files, no suppressed errors. The pipeline never buys a green build by widening types to any: where a type cannot be established with evidence, the engine emits an explicit “unsupported” marker instead. The compile rate is a floor the compiler enforces, not a claim.
The types come from a 6-phase extraction: L0 static analysis → L1 scope analysis → L2 cross-file analysis → L3 resolution → freeze → agentic inference over the remaining unknowns. After the freeze, the type universe is immutable — any post-analysis mutation throws. Later stages consume types; they cannot quietly revise them to make their own output easier.
Every typing decision lands in an evidence journal: which stage made it, in which file, at which line, and it is replayable. When a reviewer asks why a parameter came out as a specific interface rather than a guess, the answer is a journal entry, not a shrug.
The compiler stays in the loop at three granularities. Each artifact gets a per-artifact tsc check as it is emitted. Waves get batched ng build --watch rounds. The whole project gets a final fix session with no-regression snapshots, so a late fix cannot silently break an earlier file. Before any model is consulted at all, 127 deterministic auto-fix categories run first — mechanical problems get mechanical fixes.
The hardest compile problem in AngularJS codebases is the dynamic $injector lookup, where the injected name is computed at runtime. The pipeline takes a census of these, emits residue sentinels in their place, resolves them agentically with a verifier, and re-emits the result as a typed static registry. Unresolved residue blocks the run. It does not vanish into an any.
Where model assistance is used, it operates on scoped fragments under a zero-retention agreement and a US/EU-only model-provider allowlist; client code is never used for training. The conversion library itself is exercised by 45 scenario fixture projects, and 93 prompt enrichers feed the model the extracted context — types, graph edges, evidence — instead of asking it to guess.
What got flagged, and why publish 11 out of 745?
11 of 745 files could not be converted to the confidence bar the pipeline requires, so they were flagged to the issue registry — each with the file, the reason, and a suggested owner — instead of being guessed at. Publishing the number is the point: a report claiming zero flags on a 745-file production ERP is describing a guess, not a verification.
The registry philosophy is flagged, not guessed. When the pipeline cannot prove a conversion is right, the honest output is a precise description of what it could not prove and who should look at it. Three of the 11 entries from the reference run illustrate what that looks like in practice.
| Flagged item | Why it was flagged | What the owner reviews |
|---|---|---|
| jQuery jsPDF print plugin | Wrapped behind an adapter interface rather than rewritten; the legacy dependency stays isolated until its owner approves the boundary | Sign-off on the adapter before retiring the plugin |
| ACL run-block | Access-control logic ported from an AngularJS run-block to Angular route guards; guard timing differs from run-block timing | Timing review of guard execution order |
| $compile form-builder | Dynamic form builder largely converted cleanly; a small set of field types were ambiguous and are flagged rather than guessed | A decision on the ambiguous field types |
The same refusal to guess runs through the whole pipeline, not just the registry. Unconvertible template attributes are always reported as skipped, never silently converted into something plausible. A template the parser cannot handle is left byte-untouched, with a stated reason, rather than half-translated. And a coverage census reconciles input files against output: if any source file is silently dropped, the final verdict is degraded — the run cannot claim a clean result it did not earn.
The positioning follows from the mechanics: “We don’t claim turnkey. We start with proof.” A flagged list with 11 precise entries is more useful to an engineering leader than a claim of 100% automation that quietly moved the failures into production.
What does the deliverable look like at handover?
A standard Angular CLI workspace: plain Angular under strict TypeScript, no proprietary runtime, no telemetry, nothing that phones home. Alongside the code: the issue registry, the replayable evidence journal for every typing decision, and this verification report generated for your app. The engagement ends with training and handover — the codebase is yours to build in from day one.
State management is not one-size-fits-all. The pipeline supports 5 strategies — NgRx, NgRx signal-store, plain signals, services, and tiered signals — selected per codebase during the audit rather than imposed. Internally, the artifact graph tracks 24 edge types between converted artifacts, which is what makes the wave order and the registry entries precise instead of approximate.
The engagement model is audit → convert → verify → train your team → hand over. Scope is fixed and priced by the module. Your team commits about 2 hours up front; the flagged-list review is typically half a day. Regulated codebases run in a VPC deployment, so source never leaves your boundary. Published market alternatives, for contrast:
| Route | Typical published quote | Timeline | Verification evidence |
|---|---|---|---|
| Full hand rewrite | $500K+ (published industry figures) | 12–24 months | Not typically published |
| Consultancy rewrite | $40K–$150K for a mid-size app (typical published quotes) | Varies by firm | Varies by firm |
| Extended support (HeroDevs NES, OpenLogic) | $25–75K per year (vendor list pricing) | Indefinite; no migration occurs | Not applicable |
| SoftwareMigrationFactory.ai | Fixed scope, priced by the module | Reference run: 24 hours elapsed for 745 files | This report, per module |
Full cost mechanics are broken down on the migration cost page, and the trade-offs between rewriting, extended support, and pipeline migration on the options page.
The reason this is worth doing now rather than later is not subtle. AngularJS reached end-of-life in January 2022, its CVE ledger is permanent — ReDoS and sanitization bypasses that will never be patched — and PCI DSS 4.0 treats end-of-life software as a control failure. The full ledger is on the end-of-life page.
If the numbers on this page are the kind of evidence you want before committing budget, the homepage summarizes the pipeline, and the audit produces this exact report for a module of your own codebase.
What else do teams ask about this report?
Can I see a verification report for my own app?
Yes. The engagement starts with an audit that produces this same report for one module of your codebase: file census, compile rate, route verification results, and a flagged list with reasons and suggested owners. It needs about 2 hours of your team up front. Scope is fixed and priced by the module. Book a slot at calendly.com/shahulscalendar/initial-conversation or email chat@softwaremigrationfactory.ai.
Why publish the flagged number?
Because a run that reports zero flags on a 745-file production codebase is hiding guesses somewhere. The pipeline is built to surface residue, not absorb it: unconvertible template attributes are always reported skipped, never converted, and the coverage census degrades the final verdict if any source file is silently dropped. We do not claim turnkey. We start with proof.
What happens to the 11 flagged files?
Each registry entry names the file, the reason it was flagged, and a suggested owner. The client team reviews the list in a working session, typically half a day, and each item is either resolved with owner sign-off or scoped as explicit follow-up work. Nothing on the list is silently merged.
Is 24 hours the whole project?
It is the elapsed time for the pipeline run on the 745-file reference app: extraction, conversion, compile enforcement, and route verification. It does not include the audit before the run or the flagged-list review after it, which typically takes half a day of the client team. Timelines for other codebases are set per module during the audit.