refactor: manifest→IR migration — extract internal/source, decouple gwdkir, migrate buildgen#144
refactor: manifest→IR migration — extract internal/source, decouple gwdkir, migrate buildgen#144cssbruno wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a3cae21ff2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| BackendBindings: make([]manifest.BackendBinding, 0, len(ir.Endpoints)), | ||
| } |
There was a problem hiding this comment.
Preserve standalone endpoints when rebuilding the manifest
When ir.Endpoints contains standalone Go-comment endpoints (the records produced from manifest.Endpoints by addStandaloneEndpoint), this converter only turns endpoint records into BackendBindings and leaves manifest.Manifest.Endpoints empty. As a result, the new ValidateProgram/IR policy path skips the validators that read app.Endpoints (validateRouteMethodConflicts and validateStandaloneEndpoints), so invalid standalone handler names/routes or conflicts with page/API routes can pass validation when callers validate IR directly. Please reconstruct standalone endpoint declarations from ir.Endpoints in addition to backend bindings, while avoiding duplicating page block actions/APIs/fragments that are already represented under Pages.
Useful? React with 👍 / 👎.
| for _, endpoint := range ir.Endpoints { | ||
| binding := buildBackendBindingFromIR(endpoint) | ||
| binding := backendBindingFromIR(endpoint) | ||
| if binding.Status != "" || binding.ImportPath != "" || binding.FunctionName != "" { | ||
| app.BackendBindings = append(app.BackendBindings, binding) | ||
| } |
There was a problem hiding this comment.
Include load bindings in the reconstructed backend bindings
For an IR program that has a request-time load {} page plus any action/API/fragment binding, this loop only appends endpoint bindings to app.BackendBindings; the page LoadBinding is copied onto the page but never added to the binding list that ValidateBackendBindingPolicy checks. Because the presence of any endpoint binding prevents the fallback BindBackendHandlers branch from running, a production build can accept a missing or unsupported load handler as long as another backend binding exists. Add non-empty page load bindings to BackendBindings as kind load when rebuilding the manifest.
Useful? React with 👍 / 👎.
| func ValidateBackendBindingPolicyIR(config gowdk.Config, ir gwdkir.Program) error { | ||
| return ValidateBackendBindingPolicy(config, ManifestFromIR(ir)) |
There was a problem hiding this comment.
Reject unbound fragment-only IR in production
When the new IR policy is called on an unbound IR program that only declares fragment endpoints, ManifestFromIR reconstructs page.Blocks.Fragments but no BackendBindings, and the manifest fallback only rebinding-checks actions, APIs, and load blocks. In that context a production build can return nil even though the IR has a fragment endpoint with no handler metadata; ensure fragment endpoints trigger binding/policy validation before delegating through the manifest path.
Useful? React with 👍 / 👎.
…ifestFromIR Step 1 of the manifest→IR migration (see .llm/plans/manifest-to-ir-migration.md). buildgen carried its own private gwdkir→manifest converter (buildModelFromIR in internal/buildgen/ir.go) and used it solely to feed the manifest-typed compiler validators while its public entrypoints already take IR. That meant the real CLI build path validated a *reconstructed* manifest through a converter duplicated outside the package that owns validation. This moves that converter into compiler as the exported, single IR→manifest seam `compiler.ManifestFromIR`, and adds IR-first validation entrypoints `compiler.ValidateProgram` and `compiler.ValidateBackendBindingPolicyIR`. buildgen now calls the shared converter; its duplicate is deleted. Behavior-preserving: the relocated converter is byte-identical in output (git records it as a rename), and the IR path already validated the same reconstructed manifest before. New differential tests assert ValidateProgram(ir) ≡ ValidateManifest(ManifestFromIR(ir)) and that the production binding policy behaves identically on the IR path. No import cycle (buildgen already depended on compiler). Render helpers still consume manifest; migrating them to read IR directly is a later step. Full module test suite passes (46 packages, 0 failures); gofmt/vet clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
a3cae21 to
3d6d4d9
Compare
…om manifest Prerequisite for the manifest→IR migration. The shared leaf value types (SourceSpan, SourcePosition, NamedSpan, RouteParam, InlineScript, BackendInputField, BackendBindingStatus, BackendSignatureKind) lived in internal/manifest, which forced any package needing a SourceSpan to depend on the whole manifest page/component model — including internal/gwdkir, the IR that is supposed to be the manifest-independent compiler handoff. - New internal/source package holds these neutral, behavior-free types. - manifest re-exports them as type aliases, so every existing manifest.* usage keeps compiling (non-breaking extraction). - gwdkir now references source.* directly and no longer imports manifest (go list -deps: gwdkir→manifest = 0). Full module builds; compiler/buildgen/manifest suites pass; gofmt/vet clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port CachePolicy, RenderMode, HasGoBlock, DynamicParams, and TypedRouteParams (plus the RouteParamsFromPath helper chain) onto the IR Page type, mirroring the manifest.Page methods. This lets generated-output packages consume the IR page model directly instead of a reconstructed manifest. Methods read IR fields only; gwdkir remains manifest-free. Equivalence unit tests added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nifest buildgen's render/plan helpers now thread gwdkir.Page/Component/Layout/Blocks (and source.* leaf types) instead of manifest models. The IR-first entrypoints (planFromIR, SSRArtifactsFromIR, buildFromIR/buildMemoryFromIR/ buildIncrementalFromIR) iterate ir.Pages/Components/Layouts directly and validate via compiler.ValidateProgram / ValidateBackendBindingPolicyIR instead of reconstructing a manifest. Remaining manifest references in non-test buildgen are the irreducible floor for this step: - gotypes_convert.go: a field-for-field IR→manifest bridge because internal/ gotypes still consumes manifest.Import/GoTypeRef/StateContract. - Public entrypoints keep manifest.Manifest params for backward compat (gwdkanalysis.BuildIR is called internally). - SSRArtifact.LoadBinding stays manifest.BackendBinding (public output type appgen reads); converted field-for-field from gwdkir.Binding. Verified appgen reads only Status/ImportPath/PackageName/FunctionName/Signature. Full module suite: 47 packages, 0 failures. gofmt/vet clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swap manifest.* → source.* for the shared leaf types appgen uses:
BackendBinding{Bound,Missing,UnsupportedSignature} + BackendBindingStatus, all
BackendSignature* consts + BackendSignatureKind, BackendInputField, RouteParam,
SourceSpan. Six files drop their manifest import entirely.
Also renamed local identifiers named `source` (a param in ir.go, locals in
scripts.go) that would otherwise shadow the newly imported source package — no
behavior change, prevents a latent footgun.
Remaining non-test manifest references (37) are legitimate and left in place:
public manifest.Manifest entrypoints, manifest.ErrorPagePath (lives in
manifest), the goblockgen.Source API which takes []manifest.Import/GoBlock, and
the manifest.BackendBinding struct currency that carries Kind/PageID/Method/
Route fields gwdkir.Binding lacks and is threaded from manifest-input paths.
Full module suite: 47 packages, 0 failures. gofmt/vet clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… packages Sweep gwdkast, parser, gwdkanalysis, compiler, contractscan, and lsp to reference shared leaf value types (SourceSpan, SourcePosition, NamedSpan, RouteParam, InlineScript, BackendInputField, BackendBindingStatus/Signature types and consts) from internal/source instead of internal/manifest. ~400 references swapped. gwdkast and contractscan now drop the manifest import entirely. The manifest model types (Page, Component, Layout, Blocks, Manifest, ...) and manifest- resident functions (ErrorPagePath, RouteParamsFromPath, CachePolicyWithReval- idate) are intentionally left as manifest.X — the compiler legitimately validates the manifest model; migrating those is a later phase. Renamed local identifiers/params named `source` that would shadow the newly imported source package (to src/body/sourcePath). No behavior change. Full module suite: 47 packages, 0 failures. gofmt/vet clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the Compatibility Records section and the migration plan to reflect the shipped work: internal/source extraction, gwdkir/gwdkast/contractscan now manifest-free, buildgen render helpers IR-native, and leaf-type swaps across the compiler packages. Remaining work (compiler validation IR-native, AST→IR collapse, public JSON) is listed as pending. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Substantial progress on the manifest → IR migration tracked in
docs/engineering/architecture.md("Compatibility Records"). Plan:.llm/plans/manifest-to-ir-migration.md.What this does (7 commits, each independently green)
compiler.ManifestFromIR— centralize the IR→manifest converter (was a private duplicate in buildgen) + add IR-first validation entrypointsValidateProgram/ValidateBackendBindingPolicyIR.internal/source— extract the shared leaf value types (SourceSpan,SourcePosition,NamedSpan,RouteParam,InlineScript,BackendInputField,BackendBindingStatus/BackendSignatureKind+ consts) into a neutral package.manifestre-exports them as aliases (non-breaking). This was the key blocker:gwdkirpreviously depended onmanifestonly for these leaf types.gwdkirPage methods — portCachePolicy,RenderMode,HasGoBlock,DynamicParams,TypedRouteParamsonto the IR so render helpers can consume IR directly.gwdkirmodels; IR-first entrypoints validate viaValidatePrograminstead of reconstructing a manifest.internal/source; 6 files drop their manifest import.internal/source.Verified outcomes
internal/gwdkir,internal/gwdkast,internal/contractscanare now fully manifest-free (go list -depsshows 0). The IR is a manifest-independent handoff — the original goal of the compatibility-records cleanup.gotypesbridge + public-API compat types remain at the edges).scripts/test-go-modules.shgreen including nested adapter modules.gofmt/vetclean.Deliberately NOT in this PR (pending, tracked in the plan)
internal/compilerstill validates the manifest model (~980 refs) — making validation IR-native is the next large step.AST → manifest → IRtoAST → IRingwdkanalysis.Pre-existing
examples/{login,css,tailwind}build errors (main-less fixture dirs) are unrelated.🤖 Generated with Claude Code