Repository navigation
fix(#327): LoggedDataPanePrototype's 102 bindings were all on reflection — root scope, two declared template scopes, two existing item scopes - #396
Conversation
…ion — root scope, two declared template scopes, two existing item scopes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by QodoCompile bindings in LoggedDataPanePrototype
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
…sed markup, not the raw text Qodo round 1 on PR #396: a copy of either tag in a comment satisfied the substring guard while the live declaration had been repointed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 8f4f29e |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 8f4f29e |
|
Qodo-clean, CI green, ready for review. Head 8f4f29e: CI green on that SHA, and rounds 2 and 3 both clean with a settled re-check (0 unresolved qodo threads). The review grade is Fast on the final test-only delta; the view change itself was graded Balanced in round 1. Not merging. |
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 8f4f29e |
|
Qodo-clean, CI green, ready for review. Not merging.
|
|
Sweep (13-way merge build): all 13 PRs are clean together on Covered: #387 Per PR: every head matches its ready note. Every PR is
Merge build: I merged
Test count reconciles exactly: 993 on Cross-PR hazards checked:
No merge was performed and nothing was pushed to any PR branch. |
refs #327 — not merging, for review.
What was wrong
LoggedDataPanePrototype.axamlis the desktop Logged Data tab. It declared neitherx:DataTypenorx:CompileBindings, andAvaloniaUseCompiledBindingsByDefaultisfalse. So all 102 of its bindings were resolved by reflection. That count comes from parsing the file: 102{Binding}s, noMultiBinding, plus 16TemplateBindings that are not counted. A renamed or deleted view-model member would render blank and every head would still build green. For the 25IsVisibletargets it is worse: the control stays at its default oftrue. That includes the session-settings drawer and the confirm dialog, so a typo in either would pin that surface open over the pane. The loading overlay is the exception, because itsFallbackValue=Falsewould hide it.Was
mainbroken at runtime? No. I found no dead binding. Every member name compiles on this branch unchanged, and no binding path was edited. This PR is protection against future breakage. It does not fix a bug.The fix
The root gets
x:DataType="vm:DaqifiViewModel"+x:CompileBindings="True". I read the host code but did not observe it at runtime:MainWindow's Logged Data tab.ContentControl Content="{Binding}"hands the pane the window'sDaqifiViewModel.MainWindowBindingTestsalready pins this.OnLoadedand swaps in no view model. It only readsDataContext as DaqifiViewModelback inOnSessionNameChanged.The file has five scopes:
vm:DaqifiViewModel(new)ControlTemplateoxycore:TrackerHitResult(new)PlotBase.ShowTrackerassigns aTrackerHitResult. Same as #386ListBoxItemControlThemelogger:LoggingSession(new)ListBoxItem's DataContext is its item fromLoggingSessions. One declaration covers the theme'sAutomationProperties.Namesetter and the rowControlTemplateunder itDataTemplateDataType="{x:Type logger:DeviceLegendGroup}"(existing, untouched)DataTemplateDataType="{x:Type logger:LoggedSeriesLegendItem}"(existing, untouched)Both new nested declarations are required, per rule (e): a
ControlTemplateis not a new scope. I measured this by removing both declarations, which gave 13AVLN2000errors, all onDaqifiViewModel:ListBoxItemtheme: the setter'sAccessibilitySummaryplus 9 row members. The row's other 8 bindings are four bare{Binding}and four$parent[UserControl].DataContext.*commands. They compile under either scope.The five
$parent[UserControl].DataContext.*commands (four on the row, one in the legend) are unchanged.$parentis the same logical-tree hop under compiled bindings, and the compiler resolves the hop against the root'sDaqifiViewModel(see the control below). No binding was rewritten. The file opens no escape hatch.Verification
One-build positive control on both sides.
origin/main: one compilation carrying two binding typos (IsLoggingSessionSettingsOpenZZZat the root,SampleCountDisplayZZZin the row template) plus a bogusBogusPropZZZ="1"on the sameGridas the first typo. It produced 1 error, onlyBogusPropZZZat line 896. Both dead bindings were silent in the same compilation.IsLoggingSessionSettingsOpenZZZDaqifiViewModelDbLogger.PlotModelZZZDatabaseLoggerTextZZZ(tracker)OxyPlot.TrackerHitResultAccessibilitySummaryZZZ(theme setter)LoggingSessionSampleCountDisplayZZZ(row template)LoggingSession$parent[UserControl].DataContext.DisplayLoggingSessionCommandZZZ(row)DaqifiViewModel$parent[UserControl].DataContext.ToggleLoggedSeriesVisibilityCommandZZZ(legend)DaqifiViewModelTruncatedSerialNoZZZDeviceLegendGroupChannelNameZZZLoggedSeriesLegendItemRuntime: the populated pane, observed.
HeadlessBench --scripted session-10hseeds a throwawayDAQIFI_DATA_DIRwith one 108 000-sample session and opens it in the pane:LOGGED-PLOT/limitspassed.DisplaySessionButton,ExportSessionButtonandDeleteSessionButtoneach hold the shell's own command instance, with the row'sLoggingSessionasCommandParameter.ToggleLoggedSeriesVisibilityCommandwith itsLoggedSeriesLegendItemas parameter.ListBoxItem'sAutomationProperties.Nameequalssession.AccessibilitySummary.LoggedSessionStatsTextreads108000.SessionList.SelectedItemis the session, so the TwoWay binding holds.The screenshot shows the header strip, the legend and the selected row (attached).
Capture:
tools/parity-audit/run.sh --check-baselinepassed 39/39. The committedmacos-arm64.sha256is 39 lines, and the run includeddesktop-2-loggeddataanddesktop-min-2-loggeddata(the empty-state pane). I ran it alone, with no other capture running.Test:
LoggedDataPanePrototypeBindingTestsis new (+62 lines, 4 test cases).BindingFacts.csis untouched because open PR #387 edits it. The test checks:AssertRootDeclares(parsed);AssertNoEscapeHatch;x:DataTypedeclarations asparent/element[TargetType] type.Controls, each on a fresh edit and reverted:
x:CompileBindingsand repointing theListBoxItemscope failed the two tests that pin those changes.x:DataTypeadded to the legendDataTemplatealso fails.Gate: the desktop build, iOS restore, and iOS Release
iossimulator-arm64 --no-restorebuild all exited 0.dotnet testat the final head8f4f29e: 997/997. That is one fewer than the first commit's 998 because two theory cases became one fact. No lock-file changes. I did not build Android locally; CI covers it.Warnings: derived from CI's own logs at both ends, with no local rebuild.
mainat the merge base642caef(run 35377977069) vs this branch at8f4f29e(run 35398429908):third_party/excluded, is identical on every job: 0 added, 0 removed. That is 149 distinct on desktop and iOS, 151 on the library job and 153 on Android.Diff: 2 files, +90/−3. The view is +28/−3, of which +21 is the explanatory comment; the test is +62.
CI is green on
8f4f29e, the current head. Qodo:Balanced,025a1f7) raised 1 finding, conceded and fixed.Fast,8f4f29e) raised 0 findings and settled clean on both surfaces.Fast. I read the8f4f29edelta by hand: it is test-only.Fast, 0 new findings). It settled 3.5 min later with the summary unchanged and 0 unresolved qodo threads. The shepherd hand-reviewed the parsed check and ran two mutations against it, each on a fresh edit: a repointedListBoxItemscope with the old tag kept in a comment fails the nested-scope test, and unparseable markup fails all 4 tests withXmlException. Neither case skips.9-way merge of
main642caefwith #387, #389, #390, #391, #392, #393, #394, #395 and #396: a clean octopus merge, with the same tree94b85fein forward and reverse order. Desktop and iOS Release builds exit 0 with 0 errors, no lock-file changes, and 1018/1018 tests, which reconciles exactly as 993 + 0 (#387) + 3 × 7 (#389–#395) + 4 (#396). #392 and #394 add the sameIChannel.ScaleExpressionErrorin a byte-identical blob, so that file merges cleanly.🤖 Generated with Claude Code