Skip to content

refactor(layering): the Logged Data pane's markup code-behind was opening a database - #428

Merged
tylerkron merged 5 commits into
mainfrom
abstraction-police-layering
Sep 21, 2026
Merged

tylerkron merged 5 commits into
mainfrom
abstraction-police-layering

Conversation

@tylerkron

@tylerkron tylerkron commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Not merging — for review.

What the violation was costing

LoggedDataPanePrototype.axaml.cs — a markup code-behind — resolved IDbContextFactory<LoggingContext> off App.ServiceProvider in its constructor, opened a DbContext, found the row by primary key, and saved it. It was the only .axaml.cs in the repo that referenced EF Core or the service locator at all:

$ git grep -n 'App.ServiceProvider\|EntityFrameworkCore' -- '*.axaml.cs'    # on main
LoggedDataPanePrototype.axaml.cs:12:using Microsoft.EntityFrameworkCore;
LoggedDataPanePrototype.axaml.cs:36:        _loggingContext = App.ServiceProvider.GetRequiredService<IDbContextFactory<LoggingContext>>();

$ git grep -n 'App.ServiceProvider\|EntityFrameworkCore' -- '*.axaml.cs'    # on this branch
$                                                                           # exit 1 — nothing

Three concrete costs, not a style objection:

  1. It was untestable, and that hid a live bug. Nothing in Daqifi.Avalonia.Tests can construct a view, so the session-rename write was the one session write with no reachable test. Writing the tests below found that clearing a session's name has never worked — Sessions.Name is nullable: false, EF writes the backing field rather than LoggingSession.Name's substituting getter, and the save throws NOT NULL constraint failed: Sessions.Name. The code-behind logged that as an Error and showed the user nothing, so it has been filing Sentry issues while the old name quietly came back at the next launch. Filed as Clearing a logged session's name never saves — the old name comes back at the next launch #427, deliberately not fixed here (see below).
  2. The view required the app host to exist. The constructor's GetRequiredService meant the pane could not be built without a composed container.
  3. The rename could not be reached from anywhere else. It lived in a desktop view's event handler, so the mobile head could never get it without a rewrite.
graph LR
  subgraph after
    V2["LoggedDataPanePrototype<br/>.axaml.cs"] -->|PersistSessionNameAsync| LM2["LoggingManager"]
    LM2 --> DB2[("LoggingContext")]
  end
  subgraph before
    V1["LoggedDataPanePrototype<br/>.axaml.cs"] -->|GetRequiredService| SP["App.ServiceProvider"]
    V1 -->|"DbContext + Sessions.FindAsync<br/>+ SaveChangesAsync"| DB1[("LoggingContext")]
    LM1["LoggingManager"] --> DB1
  end
Loading

The fix

Move the write to LoggingManager.PersistSessionNameAsync. That is not a new layer — LoggingManager already owns LoggingSessions (the collection this very pane lists), already holds the context factory, and already has PersistSessionSampleCount, which is the same shape against the same table. The view keeps what is genuinely view work: the TextChanged handler, the 250 ms debounce, the CancellationTokenSource it cancels on the next keystroke, and the immediate in-memory update that keeps the row label live.

The simplification in one line: the view layer now has zero database knowledge — two usings, a field, a container lookup and a six-line EF block out, one call in.

raw excluding comments and blanks
production (Daqifi.Avalonia/) +33 / −11 +12 / −11

Both are worth stating: raw, this is net-additive by 22 lines, and 21 of those 22 are not code — of the 33 raw added lines, 17 are /// XML doc, 3 are // comment, 1 is a blank separator and 12 are executable; all 11 removed lines are executable. So the logic is +12 / −11: one net line, in a better place. Tests add 160 lines in one new file.

Behaviour is unchanged, and here is the evidence

  • The moved block is the same block. CreateDbContextAsync → Sessions.FindAsync([id], ct) → assign → SaveChangesAsync(ct): same order, same token, same null guard. The view's try/catch/finally is untouched, so a failure is still logged exactly where it was.

  • Visual gate: 39/39 byte-identical against the committed tools/parity-audit/baselines/macos-arm64.sha256, whose manifest holds 39 lines (git show origin/main:tools/parity-audit/baselines/macos-arm64.sha256 | wc -l → 39) — so the denominator is the whole manifest, including all nine desktop-min-* screens. That run is also what exercises the pane's construction without the removed GetRequiredService: desktop-2-loggeddata and desktop-min-2-loggeddata both render and both match.

  • Warnings: no change, measured against my own merge-base. Both sides built --no-incremental, first-party only (third_party/ excluded), de-duplicated on (file, line, col, code, message). 135 instances on each side, on both heads. The only movement is two pre-existing CS8601s relocating with the code they annotate:

    main this branch
    LoggedDataPanePrototype.axaml.cs (77,24), (93,40) (73,24)
    LoggingManager.cs — (916,28)

    Nothing added, nothing removed. (A distinct-by-file count reads 103 → 104 purely because that weaker key drops the line number while the file changed; the instance-level diff above is the honest one.)

  • Build gate green: desktop head, and iOS Release iossimulator-arm64. packages.lock.json unchanged after restore. Android not built on this host — CI covers it.

  • Tests: 1120 passed, 0 failed, whole suite, not a --filter subset.

About the tests

Four rows in Daqifi.Avalonia.Tests/Loggers/SessionRenamePersistenceTests.cs. They are characterization, not regression: the behaviour is unchanged by the move, so the usual "run them against origin/main first" check cannot apply — they name PersistSessionNameAsync, which does not exist there. That is "does not compile", not "0 failed", and none of them is evidence that the move fixed anything.

So they were mutation-tested instead, on this branch, each mutation applied to the new method alone. Four mutations, four kills, each hitting exactly the row whose name claims that behaviour:

mutation rows killed
SaveChangesAsync removed A_typed_name_reaches_the_sessions_row + Clearing_the_box_fails_to_persist_today…
name → name ?? "Session" Clearing_the_box_fails_to_persist_today… only
if (tracked != null) guard removed Renaming_a_session_that_is_no_longer_there… only
CancellationToken dropped from both awaits A_cancelled_write_leaves_the_row_alone only

The seeded row starts named "Session", so every assertion moves the value off a value it already held — an assertion that only ever sees a default proves nothing. StoredName() reads straight out of SQLite rather than back through EF, so a change that only ever reached a change-tracker cannot pass. The file was restored from backup and touched after the mutation run, and the full suite re-run green on the restored tree.

Clearing_the_box_fails_to_persist_today_and_leaves_the_old_name_in_place deliberately asserts the broken behaviour and names #427. Fixing it is a schema-or-storage decision (nullable column vs storing "") and would make this a behaviour-changing PR; when that lands, that row is the one to rewrite.

Not touched

DaqifiViewModel.cs and ConnectionDialogViewModel.cs are both under open PRs and neither is in this diff. The obvious alternative fix — routing the rename through a DaqifiViewModel command — would have needed one of them, which is why the write lands on LoggingManager instead. LoggingManager is arguably the better home regardless: it is what the mobile head would reach for.

Survey note

The rest of the layering sweep came back clean, which is worth recording so nobody re-runs it. The UI trees contain no protobuf or SCPI message construction at all — MessageProducer/MessageConsumer appear only in ConnectionManager and the three device classes (positive-controlled: the same search finds them there). Every SCPI/Protobuf hit in .axaml and in the view-models is a user-facing label, a comment, or SdCardLogFormat.Protobuf, a Core enum the user picks — Core's public surface working as intended. SdCardFailureClassifier translates Core's typed exceptions into user guidance, which is the correct shape rather than a violation.

Review

Round 1 was graded ⚖️ Balanced and raised one Medium, which was correct and conceded: the XML
doc on PersistSessionNameAsync promised that a blank name is stored as null and restores the
default label on reload — the exact behaviour this PR's own test proves does not work. I wrote that
line before running the tests that found #427 and never went back to it. The doc now states the real
contract and points at the issue.

That fix is XML doc comment lines only — the executable body is byte-identical, so round 1's
mutation controls still hold verbatim. Re-verified on the new head anyway: both builds green, full
suite 1120/0, warning set unchanged (135 each side, both heads). The visual gate was not re-run for
a comment-only change, and no rendered surface is reachable from it.

🤖 Generated with Claude Code

tylerkron and others added 4 commits September 20, 2026 10:19
…a database (WIP)

Move the session-rename write out of LoggedDataPanePrototype.axaml.cs and into
LoggingManager, which already owns the sessions the pane lists and the context
factory they live in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tylerkron

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Move session rename persistence out of Logged Data code-behind

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Move session-name persistence from Avalonia code-behind into LoggingManager.
• Preserve debounce, cancellation, missing-session, and exception behavior.
• Add SQLite tests documenting the unresolved blank-name failure tracked by #427.
Diagram

sequenceDiagram
    participant V as Logged Data pane
    participant M as LoggingManager
    participant C as LoggingContext
    participant D as Sessions database
    V->>V: Debounce name input
    V->>M: PersistSessionNameAsync
    M->>C: Create context
    C->>D: Find session
    alt Session exists
        C->>D: Save name
    end
Loading
High-Level Assessment

The chosen boundary is appropriate because LoggingManager already owns the logging-session collection and context factory, including similar session persistence behavior. A dedicated repository or new view-model command could further abstract persistence, but would add another layer without improving this focused move; the blank-name schema decision should remain a separate #427 change.

Files changed (3) +188 / -11

Refactor (2) +28 / -11
LoggingManager.csCentralize session-name persistence in LoggingManager +24/-0

Centralize session-name persistence in LoggingManager

• Adds an asynchronous session-name persistence API using the manager's existing context factory. The method honors cancellation, treats deleted sessions as a no-op, and propagates persistence failures to the caller.

Daqifi.Avalonia/Daqifi.Desktop/Loggers/LoggingManager.cs

LoggedDataPanePrototype.axaml.csRemove database access from Logged Data code-behind +4/-11

Remove database access from Logged Data code-behind

• Replaces direct service-locator and EF Core access with a call to LoggingManager. The view continues to own input trimming, blank-name normalization, debouncing, cancellation, and UI-facing error logging.

Daqifi.Avalonia/Daqifi.Desktop/View/Prototype/LoggedDataPanePrototype.axaml.cs

Tests (1) +160 / -0
SessionRenamePersistenceTests.csCharacterize session-name persistence against SQLite +160/-0

Characterize session-name persistence against SQLite

• Adds isolated database tests for successful renames, missing sessions, pre-cancelled writes, and the existing blank-name failure. Direct SQLite assertions verify that changes reach durable storage rather than only EF's change tracker.

Daqifi.Avalonia.Tests/Loggers/SessionRenamePersistenceTests.cs

@qodo-code-review

qodo-code-review Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Rename callers get false contract ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
PersistSessionNameAsync documents that a blank name is stored as null and restores the default
label on reload, although LoggingContext requires the column and SaveChangesAsync rejects that
value. When a caller passes null, the newly added test expects DbUpdateException and the old
database value remains, so the public contract describes behavior the method cannot provide.
Code

Daqifi.Avalonia/Daqifi.Desktop/Loggers/LoggingManager.cs[R893-895]

+    /// Persists the user-entered <see cref="LoggingSession.Name"/> for one session. A blank name is
+    /// stored as <c>null</c>, which is what makes the row render as "Session {ID}" again on reload.
+    /// </summary>
Relevance

●●● Strong

The documentation directly contradicts a tested NOT NULL failure; correcting the public contract is
a clear maintainability fix.

PR-#294
PR-#196

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new summary promises successful null storage, while the entity mapping marks Name required and
the implementation passes the nullable value directly to SaveChangesAsync. The new test explicitly
proves that this call throws and leaves the seeded name unchanged.

Daqifi.Avalonia/Daqifi.Desktop/Loggers/LoggingManager.cs[892-912]
Daqifi.Avalonia/Daqifi.Desktop/Loggers/LoggingContext.cs[19-31]
Daqifi.Avalonia.Tests/Loggers/SessionRenamePersistenceTests.cs[58-81]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PersistSessionNameAsync` claims that `null` is successfully stored to restore the default label, but the required `Sessions.Name` mapping rejects it and the characterization test expects that failure.

## Fix Focus Areas
- Daqifi.Avalonia/Daqifi.Desktop/Loggers/LoggingManager.cs[892-903]
- Daqifi.Avalonia.Tests/Loggers/SessionRenamePersistenceTests.cs[58-81]

## Recommended Fix
Update the XML documentation to state the current contract: passing `null` fails because the database column is required and clearing names remains tracked separately. Remove the claim that a cleared name is restored on reload; alternatively, if null clearing is intentionally made supported in this PR, change the schema or stored representation and update the characterization test accordingly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⏭️ Skipped: The latest push only revises XML documentation comments and does not change runtime behavior.

Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread Daqifi.Avalonia/Daqifi.Desktop/Loggers/LoggingManager.cs Outdated
Qodo round 1, Medium: the summary said a blank name is stored as null and
restores the default label on reload. Sessions.Name is nullable: false, so that
save throws — the very behaviour this PR's own test pins and #427 tracks. The
doc now states what the method does and points at the issue.

Comment lines only; the executable body is byte-identical, so round 1's
mutation controls still hold.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tylerkron

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 453323e

@tylerkron

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 453323e

@tylerkron

Copy link
Copy Markdown
Contributor Author

Qodo-clean and CI green on 453323e — ready for review. Not merging.

Three rounds, stated honestly rather than rounded up:

round head grade outcome
1 64673b6 ⚖️ Balanced 1 Medium — valid, conceded and fixed
2 453323e ⏭️ Skipped Bugs (0), round 1's finding ✓ Resolved
3 453323e 🚀 Fast Bugs (0) / Rule violations (0) / Cross-repo conflicts (0)

Round 2 is graded Skipped because the only push since round 1 revises XML doc comments, which is
Qodo's own stated reason — so it is not a substantive review of the code on this head, and I am
not counting it as one.
Round 3 was fired on that unchanged head for exactly that reason and came
back clean and graded, which is the independent check. A deeper grade than Fast is unreachable
here without manufacturing a code change, so I did not chase one.

What I read by hand to cover the Skipped round: the whole 64673b6..453323e delta is XML doc
comment — verified mechanically, git diff -U0 filtered to non-/// added/removed lines returns
nothing — so the executable body is byte-identical to the tree round 1 reviewed Balanced, and
round 1's four mutation controls still hold verbatim. I re-checked every factual claim the new doc
makes against source: nullable: false in 20250812090000_InitialSQLiteMigration.cs, the
substituting getter in LoggingSession.cs, and DbUpdateException as the type the test actually
asserts.

Settled before saying so: the summary has been unchanged since 20:51:11Z and re-checked 4m22s
later, with 0 unresolved qodo inline threads (author filter validated against control PR #196 →
3 threads).

CI green on this exact head, all six jobs: Desktop head on macOS, iOS head, Android head,
Library + Desktop head + capture harness, CI script self-tests, Avalonia version lockstep. The
capture-harness job is a second, independent confirmation of the 39/39 visual result in the body.

@tylerkron

Copy link
Copy Markdown
Contributor Author

Shepherd pass on 453323e — independent re-derivation

Re-deriving the two claims this PR rests on, rather than repeating them.

1. "Behaviour-preserving move onto an EXISTING seam" — holds.
No abstraction was introduced. The production diff adds exactly one method to an
already-existing type and adds no type, interface, file or registration:

  • LoggingManager on main already holds IDbContextFactory<LoggingContext> _loggingContext
    (:36, assigned :313), already owns LoggingSessions (:93) and the load/snapshot paths
    that read the same table (:757, :880), and already had PersistSessionSampleCount (:898)
    — the same shape against the same table.
  • The removed block in the code-behind (8 executable lines: CreateDbContextAsync,
    Sessions.FindAsync, the null guard, the assignment, SaveChangesAsync) reappears verbatim
    inside PersistSessionNameAsync. It moved; it was not wrapped.
  • The factory instance is the same one: LoggingManager's parameterless ctor chains to
    this(App.ServiceProvider.GetRequiredService<IDbContextFactory<LoggingContext>>()), which is
    the identical registration the pane's ctor used to resolve for itself. Same object graph, one
    fewer service-locator call in markup code-behind.
  • No .axaml markup changed (git diff --stat -- '*.axaml' is empty), consistent with the
    39/39 byte-identical visual result.

2. "The R1 fix was XML-doc-only" — holds, re-checked mechanically.
git diff -U0 64673b6 453323e, +/- lines with +++/--- dropped and /// lines filtered
out: 0 lines. Positive control — the same filter over origin/main...453323e for
LoggingManager.cs returns 12 — so the command can match, and the zero is a real zero.
One file, 15 changed lines, every one of them a /// line. The executable body is byte-identical
across that push, so round 1's mutation controls do carry over verbatim.

3. Diffstat, both ways — both correct.
Production +33 / −11 raw (LoggingManager +29/−0, LoggedDataPanePrototype +4/−11) and
+12 / −11 excluding comments and blanks. I counted both independently and they reconcile:
of the 33 raw added lines, 17 are /// doc, 3 are // comment and 1 is blank, leaving 12;
all 11 removed lines are executable. Tests +160 in one new file.
(Note for the record: the builder's own log fragment says +28/−11 raw — that figure is stale
and the PR body's +33/−11 is the correct one.)

4. Round accounting — the PR's disclosure is honest, and here is the sharper version of it.
Read off the folded blocks, not the visible summary: R1 on 64673b6 ⚖️ Balanced (1 Medium,
valid, conceded, now ✓ Resolved); R2 on 453323e ⏭️ Skipped; R3 on 453323e 🚀 Fast,
Bugs (0) / Rule violations (0) / Cross-repo conflicts (0). Disclosing R2 rather than counting
it was the right call. Worth stating plainly though: R3's own stated scope is "a single
localized documentation-only change … with no runtime behavior or independent logic paths
modified"
— so no clean round has substantively re-reviewed the production move itself;
R1 is the only round that saw it, and it raised the Medium that was fixed. That is the known
"Qodo grades the last push's delta" effect on a settled PR, not a gap the builder could have
closed without manufacturing a commit. Firing one independent round now, and hand-reviewing
the move myself either way.

5. #427 is pinned as characterization, correctly.
SessionRenamePersistenceTests asserts today's broken clearing behaviour on purpose and names
the issue; the XML doc on PersistSessionNameAsync says in so many words that a null name does
not save and points at #427. The right call — fixing it is a schema-or-storage decision and
belongs in its own PR.

Eight-way merge verification (main + #418/#419/#420/#422/#423/#425/#426/#428) reported
separately.

@tylerkron

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 453323e

@tylerkron

tylerkron commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Eight-way merge build — main + #418 #419 #420 #422 #423 #425 #426 #428

The repo's ruleset sets strict: false, so every one of these PRs displays a green that was
computed against a base none of the others were in. The merge queue is the only other thing that
would catch a bad combination, and it catches it by ejecting a PR (that is how #189 went out with
CS0115 while reading CLEAN). So: built the combination locally.

Heads — all eight match what each PR reports today:
main 4716ce1 + #418 9ad3811 + #419 b3c00d5 + #420 2ea658b + #422 a34c696 +
#423 b55a6b6 + #425 081aaa1 + #426 f8966b0 + #428 453323e.

Result

Zero conflicts in any order. Tree 2fb26b57, reproduced three ways — octopus, ascending
(418→428), descending (428→418) — all three landing on the identical tree SHA.

The ascending intermediates reproduce the three trees earlier sweeps recorded, exactly:

after tree previously recorded
+#418 #419 #420 #422 #423 8599ae34 8599ae34 ✅
+#425 fd7c5e1b fd7c5e1b ✅
+#426 1369c47c 1369c47c ✅
+#428 2fb26b57 new

Gate on the merged tree

  • Desktop head: 0 Error(s), succeeded with 150 warning(s).
  • iOS head, Release iossimulator-arm64: 0 Error(s), succeeded with 151 warning(s). Exit 0.
  • packages.lock.json unchanged after the restore (git status --short -- '**/packages.lock.json' empty).
  • Suite: 1159 passed / 0 failed / 0 skipped.
  • Visual gate: 39/39 byte-identical, all 39 expected screens captured. The denominator is the
    committed manifest's own line count — git show origin/main:tools/parity-audit/baselines/macos-arm64.sha256 | wc -l
    → 39 — so this is the whole manifest, not a subset. refactor(layering): the Logged Data pane's markup code-behind was opening a database #428 is the only one of the eight that
    touches a .axaml.cs, and no .axaml markup changed in it at all.
  • Android not built (no SDK on this host); CI covers that head per-PR.

Suite reconciliation, re-derived rather than netted

main = 1116 (CI's own log on 4716ce1, not a local run). Each delta counted off the diff by
xunit row, then closed against the measured merged total:

PR rows delta
#418 ManualWifiEndpointTests 0 → 5 +5
#419 BootloaderHoldServiceTests 20 → 17 −3 (test-negative)
#420 FirewallConfigurationTests 0 → 24 +24
#422 no test file touched 0
#423 GraphImageSaveTests 13 → 13 0
#425 ConnectionManagerConnectResultTests 5 → 8 +3
#426 ChannelsPaneRebuildTests 0 → 10 +10
#428 SessionRenamePersistenceTests 0 → 4 +4

1116 + 5 − 3 + 24 + 0 + 0 + 3 + 10 + 4 = **1159**, which is exactly the measured merged total.
It also closes from the other end: #428 standalone is 1120 in CI's log on 453323e, and
1116 + 4 = 1120.

Two traps worth naming because I walked into one of them:

Removal audit — what no file-overlap check can see

#419, #420, #422 and #425 all remove something. Every removed member and type name searched across
the whole merged tree (.cs and .axaml, since a reflection binding is not compile-checked
here), each search positive-controlled against origin/main so a zero is a real zero:

removed by symbol live references in the merged tree
#419 IBootloaderHoldService.PauseForFlashAsync 0 (one mention, in a test file's doc comment)
#420 AdminChecker, IAdminChecker, NoOpMessageBoxService, IFirewallHelper, FirewallConfiguration.Set{AdminChecker,MessageBoxService,FirewallHelper} 0 (one mention, in a code comment)
#422 SerialStreamingDevice.SendScpiMessage, SdCardSessionImporter.ImportFromStreamAsync, DaqifiViewModel.EnsureAnyDeviceConnected, DuplicateDeviceDialogViewModel.SwitchToNewInterface 0 (control on main: 2 hits in each of the 4 files)
#425 DuplicateDeviceCheckResult.IsDuplicate 0 (control on main: 7 hits)

Note #425 did not delete DuplicateDeviceCheckResult — it converts the class to a
sealed record with positional members and makes DuplicateDeviceHandler nullable, dropping
IsDuplicate in favour of returning null. Its one out-of-file consumer,
ConnectionDialogViewModel.HandleDuplicateDevice, is in #418's file — the one genuine
cross-PR coupling in this set, and it compiles. WindowsFirewallWrapper likewise survives #420 as
a concrete class; only the interface went.

Added top-level types — #423's UnixOnlyFactAttribute and the four new test classes — collide with
nothing. (#418 adds no type: CreateForManualEndpointAsync is a new public static member on the
existing DaqifiStreamingDevice, and DaqifiDeviceFactory.DefaultTcpDataPort is Core's, adopted
rather than declared here.) Two green compiles across the merged tree are the check that proves it.

Conclusion: all eight are mutually mergeable in any order, and the combination builds, tests and
renders clean.
Nothing here asks for a change to any of them.

@tylerkron

Copy link
Copy Markdown
Contributor Author

Qodo-clean, CI green — ready for review.

Not merging.

@tylerkron

Copy link
Copy Markdown
Contributor Author

The CS8601 that was routed here — measured, and it is a relocation, not a regression

Routed to this PR from the nine-way merge sweep on #431, where it was recorded only in a log
fragment and never reached this PR. Putting it here, with the measurement, because the log's
framing would otherwise turn into a "fix the warning #428 introduced" ticket, and that would be
the wrong ticket. No commit — head is still 453323e and its green is untouched.

Measured both ends in one session, Daqifi.Avalonia.Desktop head, --no-incremental,
third_party/ excluded, de-duplicated on (file, line, col, code, message text):

Tree first-party warnings of which CS8601
origin/main (4716ce1, this PR's merge-base) 135 15
this PR (453323e) 135 15

So this PR's "135 = 135" is correct, and so is the sweep's "the CS8601 is #428's, not the
merge's". Both, because the instance moved.
Diffing the two CS8601 sets rather than their
counts:

-  View/Prototype/LoggedDataPanePrototype.axaml.cs(93,40): Possible null reference assignment.
+  Loggers/LoggingManager.cs(916,28):                      Possible null reference assignment.

That is the same assignment either side. sessionToUpdate.Name = newName in the pane code-behind
became tracked.Name = name in PersistSessionNameAsync; the parameter is string? and
LoggingSession.Name is a non-nullable string, exactly as it was before the move. One site
disappeared, one appeared, the total did not budge.

Three corrections to how it was handed forward:

  1. It is not a genuinely new first-party warning. It is the pre-existing one, carried along by
    the code it annotates. Nothing on this PR made the null-assignment reachable that was not
    already reachable on main.
  2. It is not a cross-PR interaction. It reproduces on this PR standalone against its own
    merge-base, so there is nothing here the user needs to weigh before merging the stack.
  3. The method is PersistSessionNameAsync, not RenameSessionAsync.

The part worth keeping is why a count-based gate cannot see this: a one-for-one swap is
net-zero on every total, on either key, on this PR and in the merged tree. The only thing that
catches it is diffing the warning set, not the number — which is the same habit this file's
own history keeps arriving at from the other direction ("never quote a constant"). Worth adding
to the warning check rather than fixing here.

The underlying nullability is still worth an eventual fix, and the PR already documents the
behaviour it relays (#427, the null name that does not save). That is the right ticket for it;
a settled, green PR is not the place.

— shepherd for #432

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant