Background
The manifest→IR migration (PR #144) made internal/gwdkir, internal/gwdkast, and internal/contractscan fully manifest-free, migrated buildgen render helpers to IR, and moved shared leaf value types into internal/source. The remaining large coupling is internal/compiler, which still validates the manifest model (~980 manifest.* references across ~30 validator files).
This issue tracks the final phase: making compiler validation IR-native so the CLI can build IR once and validate it directly, removing the AST → manifest → validate → IR detour.
Why this is a redesign, not a mechanical swap
This was investigated and deliberately deferred from PR #144 because a naive flip to compiler.ValidateProgram(ir) silently drops two validations. IR lowering is lossy for standalone-endpoint validation:
In internal/gwdkanalysis/ir_builder.go addStandaloneEndpoint:
endpoint.Kind (raw "act"/"action"/"api") is collapsed into the 2-value gwdkir.EndpointKind — but validateStandaloneEndpoints (internal/compiler/routes.go) branches on the raw "act"/"action" strings.
endpoint.RouteSpan and endpoint.RouteParams are dropped — the validators use them for precise diagnostic spans and route-param checks.
endpoint.Method is normalized via endpointMethod(...) defaulting — but validateStandaloneEndpoints checks the raw method to emit "Go action endpoint uses unsupported method; actions currently require POST".
compiler.ManifestFromIR also does not populate manifest.Endpoints at all, so validateRouteMethodConflicts and validateStandaloneEndpoints would not run in an IR-validated path.
Net: flipping cmd/gowdk/build.go:43 and internal/lang/tools.go from ValidateManifest(app) to ValidateProgram(ir) today would lose real diagnostics.
Required work
- Enrich
gwdkir.Endpoint to carry what the endpoint validators need: RouteSpan, RouteParams ([]source.NamedSpan), and either the raw kind/method or a faithful mapping that preserves the validator's decisions. Update addStandaloneEndpoint to populate them losslessly.
- Port the validators (
validateStandaloneEndpoints, validateRouteMethodConflicts, then the broader set in validate*.go) to read gwdkir types. The IR model types already mirror manifest 1:1 for Page/Component/Layout/Blocks, so most validators are field-name swaps — but each must be verified.
- Differential guard: add tests asserting
ValidateProgram(ir) produces a byte-identical ValidationErrors set to ValidateManifest(manifest) across a fixture corpus, during a parallel-existence window, to prove zero diagnostic drift before flipping callers.
- Flip orchestration: once equivalence is proven, switch
cmd/gowdk/build.go, internal/lang/tools.go, and route_bindings.go to the IR path; ValidateManifest becomes a thin adapter or is removed.
- Keep public manifest JSON (
internal/manifest/json.go) until separately deprecated.
Acceptance
internal/compiler no longer imports internal/manifest for the model types (public-JSON aside), verified via go list -deps.
- No diagnostic regressions (differential corpus green; full
scripts/test-go-modules.sh green).
- CLI build path validates IR directly with no separate manifest threading.
References
Background
The manifest→IR migration (PR #144) made
internal/gwdkir,internal/gwdkast, andinternal/contractscanfully manifest-free, migrated buildgen render helpers to IR, and moved shared leaf value types intointernal/source. The remaining large coupling isinternal/compiler, which still validates themanifestmodel (~980manifest.*references across ~30 validator files).This issue tracks the final phase: making compiler validation IR-native so the CLI can build IR once and validate it directly, removing the
AST → manifest → validate → IRdetour.Why this is a redesign, not a mechanical swap
This was investigated and deliberately deferred from PR #144 because a naive flip to
compiler.ValidateProgram(ir)silently drops two validations. IR lowering is lossy for standalone-endpoint validation:In
internal/gwdkanalysis/ir_builder.goaddStandaloneEndpoint:endpoint.Kind(raw"act"/"action"/"api") is collapsed into the 2-valuegwdkir.EndpointKind— butvalidateStandaloneEndpoints(internal/compiler/routes.go) branches on the raw"act"/"action"strings.endpoint.RouteSpanandendpoint.RouteParamsare dropped — the validators use them for precise diagnostic spans and route-param checks.endpoint.Methodis normalized viaendpointMethod(...)defaulting — butvalidateStandaloneEndpointschecks the raw method to emit "Go action endpoint uses unsupported method; actions currently require POST".compiler.ManifestFromIRalso does not populatemanifest.Endpointsat all, sovalidateRouteMethodConflictsandvalidateStandaloneEndpointswould not run in an IR-validated path.Net: flipping
cmd/gowdk/build.go:43andinternal/lang/tools.gofromValidateManifest(app)toValidateProgram(ir)today would lose real diagnostics.Required work
gwdkir.Endpointto carry what the endpoint validators need:RouteSpan,RouteParams([]source.NamedSpan), and either the raw kind/method or a faithful mapping that preserves the validator's decisions. UpdateaddStandaloneEndpointto populate them losslessly.validateStandaloneEndpoints,validateRouteMethodConflicts, then the broader set invalidate*.go) to readgwdkirtypes. The IR model types already mirror manifest 1:1 for Page/Component/Layout/Blocks, so most validators are field-name swaps — but each must be verified.ValidateProgram(ir)produces a byte-identicalValidationErrorsset toValidateManifest(manifest)across a fixture corpus, during a parallel-existence window, to prove zero diagnostic drift before flipping callers.cmd/gowdk/build.go,internal/lang/tools.go, androute_bindings.goto the IR path;ValidateManifestbecomes a thin adapter or is removed.internal/manifest/json.go) until separately deprecated.Acceptance
internal/compilerno longer importsinternal/manifestfor the model types (public-JSON aside), verified viago list -deps.scripts/test-go-modules.shgreen).References
.llm/plans/manifest-to-ir-migration.md(Pending section)docs/engineering/architecture.md("Compatibility Records", migration-order step 4)