Repository navigation
fix: preserve Loop template manifests across hydration - #317
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe compiler now uses default-folded definitions consistently. Executed definition snapshots validate template and condition manifests after hydration. Dry-run performs this validation, and tests cover persisted snapshots, templated child loops, and invalid manifest changes. ChangesExecuted definition validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/loop/compiler.go (1)
100-101: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winLint the definition after compile-time defaults.
Compiler.Compilevalidates raw authored defaults/runtime values, lintsdef, then foldsfan_out.batch_size,run-loop.mode,goal.session, andgoal.on_exhausted. Any lint rule can reject missing default fields or warn differently from the definition used for compilation and snapshotting. Callc.linter.LintafterfoldDefinitionDefaults, or make the raw-input requirement explicit.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/loop/compiler.go` around lines 100 - 101, Update Compiler.Compile to invoke c.linter.Lint on the definition returned by foldDefinitionDefaults, ensuring linting uses the same defaults-applied definition consumed for compilation and snapshotting. Remove or relocate the earlier lint of the raw definition while preserving existing validation behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@internal/loop/compiler.go`:
- Around line 100-101: Update Compiler.Compile to invoke c.linter.Lint on the
definition returned by foldDefinitionDefaults, ensuring linting uses the same
defaults-applied definition consumed for compilation and snapshotting. Remove or
relocate the earlier lint of the raw definition while preserving existing
validation behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e721987e-3578-45a7-88fb-f85186464463
⛔ Files ignored due to path filters (6)
docs/qa/charters/CH-loop-template-snapshot-round-trip.mdis excluded by!**/*.mddocs/qa/journeys/J-01-arrive-and-use-run.mdis excluded by!**/*.mddocs/qa/reports/2026-08-05-issue-313-loop-manifest.mdis excluded by!**/*.mddocs/qa/scenarios/LP-loop-template-snapshot-round-trip.mdis excluded by!**/*.mdpackages/site/content/docs/loops/authoring.mdxis excluded by!**/*.mdxskills/compozy/references/loops.mdis excluded by!**/*.md
📒 Files selected for processing (8)
internal/loop/compiler.gointernal/loop/coordinator_snapshot_test.gointernal/loop/executed_definition_hydrate.gointernal/loop/executed_definition_manifest.gointernal/loop/executed_definition_snapshot.gointernal/loop/service.gointernal/loop/service_integration_test.gointernal/loop/service_test.go
💤 Files with no reviewable changes (1)
- internal/loop/executed_definition_hydrate.go
|
| Filename | Overview |
|---|---|
| internal/loop/compiler.go | Canonicalizes defaults once and consistently uses the folded definition throughout compilation. |
| internal/loop/executed_definition_snapshot.go | Adds production-loader round-trip validation during snapshot construction and precise manifest comparisons during loading. |
| internal/loop/executed_definition_manifest.go | Implements deterministic, key-specific comparisons for template and condition manifests. |
| internal/loop/service.go | Extends dry-run to validate the same executed-definition snapshot boundary used by real submission. |
| internal/loop/coordinator_snapshot_test.go | Adds focused coverage for templated parent and child Loop snapshot hydration. |
| internal/loop/service_integration_test.go | Verifies workspace-scoped persistence and hydration of default modes and child templates. |
| internal/loop/service_test.go | Verifies dry-run rejects non-round-trippable snapshots without creating state. |
Sequence Diagram
sequenceDiagram
participant User
participant Service as Loop Service
participant Compiler
participant Snapshot as Snapshot Builder
participant Loader as Production Loader
participant Store
User->>Service: Dry-run or Start
Service->>Compiler: Compile definition
Compiler->>Compiler: Fold defaults and compile canonical form
Compiler-->>Service: Resolved definition + manifests
Service->>Snapshot: Build canonical snapshot
Snapshot->>Loader: Load snapshot with computed digest
Loader->>Compiler: Recompile persisted definition
Loader->>Loader: Compare template and condition manifests
Loader-->>Snapshot: Hydrated definition or precise validation error
alt Dry-run
Snapshot-->>Service: Validated snapshot (discard bytes)
Service-->>User: Plan preview
else Start
Snapshot-->>Service: Snapshot bytes + digest
Service->>Store: Persist Run and snapshot
Service-->>User: Created Run
end
Reviews (1): Last reviewed commit: "fix: preserve Loop template manifests ac..." | Re-trigger Greptile
Context and reproduction
Issue #313 exposed a mismatch between a Loop definition that could validate/dry-run and the executed-definition snapshot used when a real Run was submitted. A valid parent Loop with two
run-loopchildren, templated child inputs, references to a prior node output, and omitted childmodevalues compiled successfully. Submission then failed while hydrating the persisted definition withexecuted definition template manifest changed.The focused regression reproduced the exact drift:
nodes.workspace_child.params.modeandnodes.marketplace_child.params.mode, both with sourceawait.This meant a definition accepted before submission could not cross the build → persistence → hydration boundary required to create and retain a Run.
Proven root cause
Compiler.CompilestoredfoldDefinitionDefaults(def)inResolvedDefinition.Definition, but linting and every template/condition compilation phase still consumed the original, unfoldeddef.The snapshot therefore combined two different representations:
On hydration, the daemon correctly recompiled the persisted definition. The default
awaitstrings were then discovered as template sources, so the integrity comparison rejected the snapshot. The integrity check was revealing a real compiler inconsistency; it was not the cause and has not been weakened.Solution design
BuildExecutedDefinitionSnapshotvalidate its own canonical output through the production loader before returning bytes or a digest. A snapshot that cannot round-trip can no longer reach persistence.Changes by surface
Runtime and persistence
internal/loop/compiler.go.internal/loop/executed_definition_manifest.goand kept production files below the 500-line limit.CLI, HTTP, UDS, native tools, and web
compozy loop run --dry-runandcompozy__loop_runwithdry: truenow exercise the same executed-definition snapshot boundary used by a real run.Documentation and agent operation
compozyskill so agents know that dry-run proves snapshot save/load before publication or token spend.Invariants and canonical test ownership
internal/loop/coordinator_snapshot_test.go.internal/loop/service_test.go.internal/loop/service_integration_test.go.Focused evidence:
go test -race ./internal/loop -run 'TestCoordinator...|TestServiceDryRun...|TestServiceParticipation...' -count=1— 8 tests passed.go test -race -tags=integration ./internal/loop -run 'TestServiceIntegrationExecutedDefinitionSnapshot/...' -count=1— 2 tests passed.make gate-full— PASS, fingerprintd57067525fcf8439c110d219821c27aa3ff864fb; 20,689 Go tests passed with 2 documented skips, plus codegen, installer, source-size, Bun lint/typecheck/test/build, Go format/lint/build, and package boundaries.Real-scenario QA and evidence
QA used a fresh isolated runtime with a unique
COMPOZY_HOME, HTTP port52702, UDS path, and extension process envelope.workspace-childcame from the workspace andreview-and-fixcame from the marketplace.valid: true.looprun-4b66e31d8f935186with digestsha256:73a51e3b67426d74ce8a9749973e038819463c549e62f77a83353f42aa1eaf85.mode: await, parent-input templates, and prior-node-output templates.teardown.jsonreports"clean": true, both registered processes were terminated, and survivors are empty.Scenario:
docs/qa/scenarios/LP-loop-template-snapshot-round-trip.mdReport:
docs/qa/reports/2026-08-05-issue-313-loop-manifest.mdCompozy Impact Audit
compozy__loop_rundry-run/start paths. Checkedcompozy__loop_validate,compozy__loop_run,compozy__loop_runs, and their CLI/HTTP/UDS fallbacks. No tool IDs, toolsets, descriptors, I/O schemas, schema digests, risk flags, availability diagnostics, or capability gates changed.config.tomlkeys/defaults/lifecycle changed; the compiler/service boundary is shared by those sources.skills/compozy/references/loops.mdto document the dry-run round-trip guarantee and exact key/source diagnostics.Compatibility, risks, and decisions
loop_requires_liveerror contract.Fixes #313
Summary by CodeRabbit
Bug Fixes
Reliability