You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This started on #337 (Onboarding Existing Projects to Agentic Engineer).
Went through the codebase before writing this up, since the shape of the solution really depends on what's already there.
The concern with a single top-down pipeline
Scanning the existing code, generating a spec/design/tasks against platform architecture, and regenerating everything unconditionally has a few problems:
Token cost is proportional to the whole repo, on top of what greenfield already costs. For anything beyond a small codebase this adds up fast.
There's no way to detect drift until a regression ships. Nothing checks the regenerated behavior against what the app actually did before.
It assumes every onboarded project should become Ballerina/Go for backend and React for frontend. Worth noting the high-level-architecture skill's actual default is Ballerina for backends, anything else only when requirements name it, so the constraint isn't quite what it looks like at first, but it's still a constraint: onboarding an existing stack and rewriting it into ours are two different asks, and right now the design only supports the second one.
Proposal: a new front door into the pipeline, not a new pipeline
Onboarding doesn't need new agents or a new execution model. It needs a new Skill that produces the same artifacts start -> design already produces (requirements.md, design.cell, per-component design.json, validation-criteria.json), just derived from reverse-engineering an existing codebase instead of an interview. Task-planning, milestone execution, and validation stay exactly as they are.
Per component (AEP's actual unit, not something new), the design records one of two intents:
Import as-is
Modernize
What's generated
Nothing, the component is registered, not rebuilt
A full component, via the same design/task-planning skills used for greenfield
Executor
ops (registration/wiring, same executor class that provisions a database or an IdP app today)
coding, same as any greenfield component
Where it deploys
OpenChoreo's deploy-from-image path, if the legacy app is (or can be) containerized as-is
A normal AEP-built component
Stack
Whatever it already is, never touched
Whatever the design skill decides, same as greenfield
Cost
Low, one-time
Higher, proportional to how much gets modernized, but opt-in per component instead of mandatory for the whole project
Default posture: everything imports as-is unless flagged otherwise. If someone already knows a specific component needs a rewrite at onboarding time, they can flag it as modernize right there, nothing blocks that. Anything not explicitly flagged imports as-is, so onboarding completes without forcing a decision on every component up front. Either way, modernize is always revisitable later, per component, as its own action. Since this is just a field on design.json, none of it needs a separate hybrid mode built.
How components actually get identified
Static analysis (AST parsing, call graphs, module boundaries) produces evidence about the codebase: what calls what, where the natural seams are, what the entry points are. It doesn't decide component boundaries on its own, and it shouldn't. That decision goes through the same mechanism greenfield already uses: the generic agent, running the high-level-architecture skill, which has explicit rules for what counts as a legitimate component (one independently deployable unit, not a domain split, not a layer split, not "whatever packages already exist"). For greenfield, that skill's input is a human's stated requirements. For onboarding, its input is the static-analysis output instead.
So the flow is: static analysis produces facts about the codebase, those facts feed an agent turn running the same high-level-architecture skill, the skill applies its normal justification rules to those facts and proposes a decomposition, and that becomes design.cell and the per-component design.jsons, same output shape as greenfield always produces.
The main open risk here: a legacy monolith's actual internal package structure (split by domain object, or by layer like auth/notifications/storage as separate folders) is exactly the kind of boundary the skill says not to use for components. The static analysis itself is mechanical and well understood. The real question is whether the agent correctly avoids just reproducing the legacy folder structure when it doesn't pass the "independently deployable unit" test, which is worth prototyping against a real monolith early rather than assuming it falls out for free.
The parity check
This is what makes modernize safe, and it doesn't need new infrastructure:
The onboarding skill authors validation-criteria.json entries from the legacy component's observed behavior, same schema as today.
For a modernized component's milestone, the platform resolves two endpoint sets (legacy and new) instead of one, and dispatches the existing validation task kind against both.
Same e2e suite either way, two report.json outputs. Diff them. Cut over on match, fix and re-run on mismatch.
No new runner, no new container, and no comparison running inside the coding-agent pod itself, since ADR-0011/0012 already ruled that out (in-pod build verification was tried and dropped for the rootless-podman-vs-secrets-in-the-pod problem). This reuses the existing Playwright/report.json/regression-set machinery, just pointed at two targets instead of one. Live traffic mirroring at the gateway could be a later enhancement, but the above doesn't depend on it.
Discovered dependencies
Whatever reverse-engineering finds (a legacy call to Stripe, a call to another internal service) becomes an ordinary dependencies[] entry on the relevant design.json: external for third-party (resolved live per ADR-0010, not pre-stored), component for another component in the same onboarded project, org-service for a first-party service elsewhere. Nothing new needed here either.
What's actually new to build
The onboarding skill itself: reverse-engineer -> the four artifacts above.
Static-analysis tooling to feed it. Can run inside the existing runner image (already has Go, Node, git), no new container needed. Start with Go/TS/Ballerina, extend as demand shows up.
A per-component import-as-is / modernize flag threaded from design.json through task-planning.
Platform-side support for resolving two endpoint sets for a migrating component's validation run and diffing the reports.
Open questions
Component boundaries for a legacy monolith aren't a pure graph-partitioning problem, as above. Worth a small prototype against a real codebase before committing to the approach.
Deploy-from-image checks out: OpenChoreo's Bring Your Own Image flow is exactly this, a Component with no build config paired with a Workload resource pointing container.image at an already-built image. Public registries need no extra setup; private registries need pull credentials wired through a secret store (OpenBao by default) plus an ExternalSecret on the ClusterComponentType.
Happy to break this into sub-tasks if there's agreement on direction.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
This started on #337 (Onboarding Existing Projects to Agentic Engineer).
Went through the codebase before writing this up, since the shape of the solution really depends on what's already there.
The concern with a single top-down pipeline
Scanning the existing code, generating a spec/design/tasks against platform architecture, and regenerating everything unconditionally has a few problems:
high-level-architectureskill's actual default is Ballerina for backends, anything else only when requirements name it, so the constraint isn't quite what it looks like at first, but it's still a constraint: onboarding an existing stack and rewriting it into ours are two different asks, and right now the design only supports the second one.Proposal: a new front door into the pipeline, not a new pipeline
Onboarding doesn't need new agents or a new execution model. It needs a new Skill that produces the same artifacts
start->designalready produces (requirements.md,design.cell, per-componentdesign.json,validation-criteria.json), just derived from reverse-engineering an existing codebase instead of an interview. Task-planning, milestone execution, and validation stay exactly as they are.Per component (AEP's actual unit, not something new), the design records one of two intents:
Default posture: everything imports as-is unless flagged otherwise. If someone already knows a specific component needs a rewrite at onboarding time, they can flag it as modernize right there, nothing blocks that. Anything not explicitly flagged imports as-is, so onboarding completes without forcing a decision on every component up front. Either way, modernize is always revisitable later, per component, as its own action. Since this is just a field on
design.json, none of it needs a separate hybrid mode built.How components actually get identified
Static analysis (AST parsing, call graphs, module boundaries) produces evidence about the codebase: what calls what, where the natural seams are, what the entry points are. It doesn't decide component boundaries on its own, and it shouldn't. That decision goes through the same mechanism greenfield already uses: the generic agent, running the
high-level-architectureskill, which has explicit rules for what counts as a legitimate component (one independently deployable unit, not a domain split, not a layer split, not "whatever packages already exist"). For greenfield, that skill's input is a human's stated requirements. For onboarding, its input is the static-analysis output instead.So the flow is: static analysis produces facts about the codebase, those facts feed an agent turn running the same
high-level-architectureskill, the skill applies its normal justification rules to those facts and proposes a decomposition, and that becomesdesign.celland the per-componentdesign.jsons, same output shape as greenfield always produces.The main open risk here: a legacy monolith's actual internal package structure (split by domain object, or by layer like auth/notifications/storage as separate folders) is exactly the kind of boundary the skill says not to use for components. The static analysis itself is mechanical and well understood. The real question is whether the agent correctly avoids just reproducing the legacy folder structure when it doesn't pass the "independently deployable unit" test, which is worth prototyping against a real monolith early rather than assuming it falls out for free.
The parity check
This is what makes modernize safe, and it doesn't need new infrastructure:
validation-criteria.jsonentries from the legacy component's observed behavior, same schema as today.validationtask kind against both.report.jsonoutputs. Diff them. Cut over on match, fix and re-run on mismatch.No new runner, no new container, and no comparison running inside the coding-agent pod itself, since ADR-0011/0012 already ruled that out (in-pod build verification was tried and dropped for the rootless-podman-vs-secrets-in-the-pod problem). This reuses the existing Playwright/
report.json/regression-set machinery, just pointed at two targets instead of one. Live traffic mirroring at the gateway could be a later enhancement, but the above doesn't depend on it.Discovered dependencies
Whatever reverse-engineering finds (a legacy call to Stripe, a call to another internal service) becomes an ordinary
dependencies[]entry on the relevantdesign.json:externalfor third-party (resolved live per ADR-0010, not pre-stored),componentfor another component in the same onboarded project,org-servicefor a first-party service elsewhere. Nothing new needed here either.What's actually new to build
import-as-is/modernizeflag threaded fromdesign.jsonthrough task-planning.Open questions
Happy to break this into sub-tasks if there's agreement on direction.
All reactions