chore: ConfigureAwait, comment compaction and public API tracking #177

Merged
Grandiras merged 5 commits from issue-139-chore into main 2026-09-27 10:54:56 +00:00
Owner

Closes #139.

Four mechanical, behavior-preserving passes over src/ (library projects only — tests/ and samples/ are untouched except a couple of trivial using removals noted below).

1. ConfigureAwait(false) on every library await

  • src/.editorconfig enables CA2007 (dotnet_diagnostic.CA2007.severity = error), and src/Directory.Build.props adds CA2007 to WarningsAsErrors alongside CS1591, so a missed ConfigureAwait(false) fails the build the same way an undocumented public member does.
  • Applied via dotnet format analyzers --diagnostics CA2007 per project, then hand-reviewed. ~518 call sites gained .ConfigureAwait(false), covering await, await foreach (on the enumerable) and await using (on the disposable).
  • ATProtoNet.Blazor components are untouched. ComposePost, FeedView, LoginForm, PostCard and ProfileCard live in .razor files and call StateHasChanged-equivalent UI updates after their awaits; CA2007 does not fire on Razor's generated code (confirmed empirically — the analyzer skips it), so no config was needed to exempt them. AtProtoUserClientAccessor, the project's one non-component service with awaits, took ConfigureAwait(false) throughout: nothing after its awaits touches circuit state.
  • A real bug in the CA2007 code fix, worked around by hand. For await using var x = await Expr();, the automated fixer configures both the inner task await and the outer implicit DisposeAsync await by wrapping the whole initializer a second time — which changes x's declared type to ConfiguredAsyncDisposable, breaking any later use of its real members (9 compile errors surfaced this in ATProtoNet/ATProtoNet.Server, another 31 in EF Core's CreateDbContextAsync call sites). Fixed by keeping ConfigureAwait(false) only on the explicit task await and suppressing CA2007 for the now-unconfigured disposal with a one-line-reasoned #pragma warning disable/restore CA2007 (35 sites total). This is a legitimate C# limitation (there is no way to configure a disposal-only await without changing the declared type in that combined statement form), not a policy choice.

2. Comment compaction

  • Collapsed 1,679 three-line /// <summary> blocks (single-sentence body, ~120–130 chars) onto one line, across 275+ files.
  • Collapsed 193 three-line box-drawing section dividers into the one-line style already used elsewhere in the codebase (// ── Title ──────...), same total width as the original box, across 41 files.
  • Dropped 683 <param> tags that only restated the parameter's name in words (e.g. <param name="cancellationToken">Cancellation token.</param>, <param name="space">The space.</param>), determined algorithmically (description, minus a leading article and trailing period, equals the parameter name split into words) so only true tautologies were removed — anything carrying real information ("The DID of the repository.", "Cancellation token to stop consuming.") was left alone. src/Directory.Build.props adds NoWarn CS1573, since dropping some but not all of a member's <param> tags would otherwise warn.
  • Searched for history-style comments (previously…, used to be…, was changed…) and obvious-code narration: none found. The codebase already followed CLAUDE.md's existing comment conventions.
  • Nothing already-fine was reflowed or reworded.

3. Public API tracking

  • Added Microsoft.CodeAnalysis.PublicApiAnalyzers (5.6.0) to src/Directory.Build.props, scoped to packable projects, PrivateAssets="all" (confirmed it does not leak into the published .nuspec).
  • PublicAPI.Shipped.txt / PublicAPI.Unshipped.txt added to all five packable projects: ATProtoNet (8,966 lines), ATProtoNet.Server (630), ATProtoNet.Server.EntityFrameworkCore (155), ATProtoNet.Blazor (165, including Razor components' [Parameter] properties and lifecycle overrides — see below), ATProtoNet.Aspire.Hosting (62). Shipped.txt is the pre-0.7.0 baseline (generated via the analyzer's own RS0016 code fix); Unshipped.txt is empty.
  • RS0016 (undeclared public member) and RS0017 (removed public member) are promoted to build errors in WarningsAsErrors, next to CS1591/CA2007.
  • RS0026/RS0027 (the "overloads with optional parameters" backcompat lint, also shipped in this package) are silenced: the SDK already has many such overloads by design (a trailing CancellationToken, mostly), and redesigning that is unrelated to this issue.
  • ATProtoNet.Blazor's Razor components are genuinely tracked, unlike with CA2007. RS0016 does not skip Razor-generated code, and correctly so — a component's [Parameter] properties and event callbacks are real public surface. dotnet format analyzers itself can't reach that generated code (it only edits physical source files), so those 94 symbols were added to PublicAPI.Shipped.txt by hand from the build's own error list. The generated BuildRenderTree override also reports RS0041 ("oblivious reference types"); an .editorconfig severity override can't reach it either (its diagnostics are keyed to the obj/ source-generator output path, not the source tree's .editorconfig hierarchy), so it's suppressed with <NoWarn>RS0041</NoWarn> directly in ATProtoNet.Blazor.csproj.
  • tools/ATProtoNet.LexiconGenerator deliberately has no PublicAPI.*.txt. It packs as a dotnet tool (PackAsTool), consumed through its CLI (atproto-lexgen), not by referencing its assembly as a library — there is no consumer contract for RS0016/RS0017 to protect, even though the assembly does have many public types.
  • CONTRIBUTING.md gains a short "Public API tracking" section describing the Shipped/Unshipped workflow (adding API → Unshipped; a release moves Unshipped → Shipped).

4. Unused using directives

  • Enabled IDE0005 at warning severity in src/.editorconfig and ran dotnet format style --diagnostics IDE0005 across every src/ project, the LexiconGenerator tool, and the samples/ projects not owned by the parallel test-suite work. The codebase was already close to clean: 9 stale usings removed across 9 files (one each).
  • tests/** was left alone entirely, per this issue's scope-discipline note (the parallel #137 test-suite agent is editing many test files there).

Breaking changes

None. All four passes are behavior-preserving by construction (comment/whitespace/analyzer-config only, plus .ConfigureAwait(false) which does not change return values or exception behavior — only which context a continuation resumes on).

Verified

  • Full solution build (dotnet build -p:EnableSourceControlManagerQueries=false): 0 warnings, 0 errors.
  • dotnet test tests/ATProtoNet.Tests/: 5,045 passed, 5 skipped, 0 failed — run twice, identical both times.
  • dotnet pack -c Release succeeds for all five packable projects (ATProtoNet, ATProtoNet.Server, ATProtoNet.Server.EntityFrameworkCore, ATProtoNet.Blazor, ATProtoNet.Aspire.Hosting).
  • Confirmed Microsoft.CodeAnalysis.PublicApiAnalyzers does not appear as a dependency in the packed .nuspec (PrivateAssets="all" works as intended).
  • Diff-scanned the entire comment-compaction commit programmatically: every changed line in src/ is a ////// comment line, a blank line, or the new NoWarn entry's own explanatory <!-- --> comment in Directory.Build.props — no code line changed.

Deferred / flagged

  • tests/** and samples/** usings beyond the ones already clean were not swept exhaustively, per scope discipline around the parallel #137 (tests) work.
  • The Blazor package's public-API baseline includes ATProtoNet.Blazor._Imports (a public class Razor generates from _Imports.razor, which carries no real API). This reflects the actual current surface rather than something introduced by this issue; tightening component visibility (e.g. via code-behind + explicit access modifiers) is a separate, non-mechanical change outside this issue's scope.

🤖 Generated with Claude Code

Closes #139. Four mechanical, behavior-preserving passes over `src/` (library projects only — `tests/` and `samples/` are untouched except a couple of trivial `using` removals noted below). ## 1. `ConfigureAwait(false)` on every library await - `src/.editorconfig` enables `CA2007` (`dotnet_diagnostic.CA2007.severity = error`), and `src/Directory.Build.props` adds `CA2007` to `WarningsAsErrors` alongside `CS1591`, so a missed `ConfigureAwait(false)` fails the build the same way an undocumented public member does. - Applied via `dotnet format analyzers --diagnostics CA2007` per project, then hand-reviewed. ~518 call sites gained `.ConfigureAwait(false)`, covering `await`, `await foreach` (on the enumerable) and `await using` (on the disposable). - **`ATProtoNet.Blazor` components are untouched.** `ComposePost`, `FeedView`, `LoginForm`, `PostCard` and `ProfileCard` live in `.razor` files and call `StateHasChanged`-equivalent UI updates after their awaits; CA2007 does not fire on Razor's generated code (confirmed empirically — the analyzer skips it), so no config was needed to exempt them. `AtProtoUserClientAccessor`, the project's one non-component service with awaits, took `ConfigureAwait(false)` throughout: nothing after its awaits touches circuit state. - **A real bug in the CA2007 code fix, worked around by hand.** For `await using var x = await Expr();`, the automated fixer configures *both* the inner task await and the outer implicit `DisposeAsync` await by wrapping the whole initializer a second time — which changes `x`'s declared type to `ConfiguredAsyncDisposable`, breaking any later use of its real members (9 compile errors surfaced this in `ATProtoNet`/`ATProtoNet.Server`, another 31 in EF Core's `CreateDbContextAsync` call sites). Fixed by keeping `ConfigureAwait(false)` only on the explicit task await and suppressing CA2007 for the now-unconfigured disposal with a one-line-reasoned `#pragma warning disable/restore CA2007` (35 sites total). This is a legitimate C# limitation (there is no way to configure a disposal-only await without changing the declared type in that combined statement form), not a policy choice. ## 2. Comment compaction - Collapsed 1,679 three-line `/// <summary>` blocks (single-sentence body, ~120–130 chars) onto one line, across 275+ files. - Collapsed 193 three-line box-drawing section dividers into the one-line style already used elsewhere in the codebase (`// ── Title ──────...`), same total width as the original box, across 41 files. - Dropped 683 `<param>` tags that only restated the parameter's name in words (e.g. `<param name="cancellationToken">Cancellation token.</param>`, `<param name="space">The space.</param>`), determined algorithmically (description, minus a leading article and trailing period, equals the parameter name split into words) so only true tautologies were removed — anything carrying real information (`"The DID of the repository."`, `"Cancellation token to stop consuming."`) was left alone. `src/Directory.Build.props` adds `NoWarn CS1573`, since dropping some but not all of a member's `<param>` tags would otherwise warn. - Searched for history-style comments (`previously…`, `used to be…`, `was changed…`) and obvious-code narration: **none found.** The codebase already followed `CLAUDE.md`'s existing comment conventions. - Nothing already-fine was reflowed or reworded. ## 3. Public API tracking - Added `Microsoft.CodeAnalysis.PublicApiAnalyzers` (5.6.0) to `src/Directory.Build.props`, scoped to packable projects, `PrivateAssets="all"` (confirmed it does not leak into the published `.nuspec`). - `PublicAPI.Shipped.txt` / `PublicAPI.Unshipped.txt` added to all five packable projects: `ATProtoNet` (8,966 lines), `ATProtoNet.Server` (630), `ATProtoNet.Server.EntityFrameworkCore` (155), `ATProtoNet.Blazor` (165, including Razor components' `[Parameter]` properties and lifecycle overrides — see below), `ATProtoNet.Aspire.Hosting` (62). `Shipped.txt` is the pre-0.7.0 baseline (generated via the analyzer's own RS0016 code fix); `Unshipped.txt` is empty. - `RS0016` (undeclared public member) and `RS0017` (removed public member) are promoted to build errors in `WarningsAsErrors`, next to `CS1591`/`CA2007`. - `RS0026`/`RS0027` (the "overloads with optional parameters" backcompat lint, also shipped in this package) are silenced: the SDK already has many such overloads by design (a trailing `CancellationToken`, mostly), and redesigning that is unrelated to this issue. - **`ATProtoNet.Blazor`'s Razor components are genuinely tracked, unlike with CA2007.** RS0016 does *not* skip Razor-generated code, and correctly so — a component's `[Parameter]` properties and event callbacks are real public surface. `dotnet format analyzers` itself can't reach that generated code (it only edits physical source files), so those 94 symbols were added to `PublicAPI.Shipped.txt` by hand from the build's own error list. The generated `BuildRenderTree` override also reports `RS0041` ("oblivious reference types"); an `.editorconfig` severity override can't reach it either (its diagnostics are keyed to the `obj/` source-generator output path, not the source tree's `.editorconfig` hierarchy), so it's suppressed with `<NoWarn>RS0041</NoWarn>` directly in `ATProtoNet.Blazor.csproj`. - **`tools/ATProtoNet.LexiconGenerator` deliberately has no `PublicAPI.*.txt`.** It packs as a `dotnet tool` (`PackAsTool`), consumed through its CLI (`atproto-lexgen`), not by referencing its assembly as a library — there is no consumer contract for RS0016/RS0017 to protect, even though the assembly does have many public types. - `CONTRIBUTING.md` gains a short "Public API tracking" section describing the Shipped/Unshipped workflow (adding API → Unshipped; a release moves Unshipped → Shipped). ## 4. Unused `using` directives - Enabled `IDE0005` at warning severity in `src/.editorconfig` and ran `dotnet format style --diagnostics IDE0005` across every `src/` project, the `LexiconGenerator` tool, and the `samples/` projects not owned by the parallel test-suite work. The codebase was already close to clean: 9 stale `using`s removed across 9 files (one each). - `tests/**` was left alone entirely, per this issue's scope-discipline note (the parallel #137 test-suite agent is editing many test files there). ## Breaking changes None. All four passes are behavior-preserving by construction (comment/whitespace/analyzer-config only, plus `.ConfigureAwait(false)` which does not change return values or exception behavior — only which context a continuation resumes on). ## Verified - Full solution build (`dotnet build -p:EnableSourceControlManagerQueries=false`): **0 warnings, 0 errors**. - `dotnet test tests/ATProtoNet.Tests/`: **5,045 passed, 5 skipped, 0 failed** — run twice, identical both times. - `dotnet pack -c Release` succeeds for all five packable projects (`ATProtoNet`, `ATProtoNet.Server`, `ATProtoNet.Server.EntityFrameworkCore`, `ATProtoNet.Blazor`, `ATProtoNet.Aspire.Hosting`). - Confirmed `Microsoft.CodeAnalysis.PublicApiAnalyzers` does not appear as a dependency in the packed `.nuspec` (`PrivateAssets="all"` works as intended). - Diff-scanned the entire comment-compaction commit programmatically: every changed line in `src/` is a `//`/`///` comment line, a blank line, or the new `NoWarn` entry's own explanatory `<!-- -->` comment in `Directory.Build.props` — no code line changed. ## Deferred / flagged - `tests/**` and `samples/**` usings beyond the ones already clean were not swept exhaustively, per scope discipline around the parallel #137 (tests) work. - The Blazor package's public-API baseline includes `ATProtoNet.Blazor._Imports` (a public class Razor generates from `_Imports.razor`, which carries no real API). This reflects the actual current surface rather than something introduced by this issue; tightening component visibility (e.g. via code-behind + explicit access modifiers) is a separate, non-mechanical change outside this issue's scope. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Enforces CA2007 as an error in src/ (via src/.editorconfig and
src/Directory.Build.props), so a missed ConfigureAwait fails the
build. This matters for Blazor Server, WPF and MAUI hosts, which have
a synchronization context the SDK must not resume on; ASP.NET Core
has none, so Server code was already safe.

ATProtoNet.Blazor's components (ComposePost, FeedView, LoginForm,
PostCard, ProfileCard) live in .razor files and are untouched: they
call StateHasChanged after awaiting and need the captured context.
AtProtoUserClientAccessor, the project's one non-component service
with awaits, gets ConfigureAwait(false) throughout since nothing
after its awaits touches circuit state.

The analyzer's code fix mishandles `await using var x = await y();`:
configuring the disposal await changes x's declared type to
ConfiguredAsyncDisposable, breaking later use of x's real members.
Where that happened (mostly EF Core's CreateDbContextAsync call
sites, plus a few Stream/response cases), only the explicit await is
configured and the implicit dispose keeps the default context via a
one-line-reasoned CA2007 suppression.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Enables IDE0005 (unused using) at warning severity in src/.editorconfig
and runs `dotnet format style --diagnostics IDE0005` across every src
project and the LexiconGenerator tool, plus the samples that are not
owned by the parallel test-suite work. The codebase was already close
to clean: 9 files had a stale using.

CHANGELOG is not updated for this commit per the issue's own scope
(no user-visible or API change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds Microsoft.CodeAnalysis.PublicApiAnalyzers to every packable
project (ATProtoNet, ATProtoNet.Server, ATProtoNet.Server.EntityFrameworkCore,
ATProtoNet.Blazor, ATProtoNet.Aspire.Hosting) via src/Directory.Build.props,
with PublicAPI.Shipped.txt seeded from the analyzer's own RS0016 code
fix as the pre-0.7.0 baseline and PublicAPI.Unshipped.txt left empty.
RS0016 (undeclared public member) and RS0017 (removed public member)
are promoted to build errors alongside CS1591 and CA2007, so a future
API change that isn't reflected in Unshipped.txt fails the build.

RS0026/RS0027 (overloads-with-optional-parameters backcompat lint) are
silenced: the SDK already has many such overloads by design (a trailing
CancellationToken, mostly), and redesigning that is unrelated to this
issue's public-API-tracking goal.

The tool project, tools/ATProtoNet.LexiconGenerator, is deliberately
left out: it packs as a dotnet tool (PackAsTool), consumed by its CLI
surface, not by referencing its assembly, so there is no consumer
contract for RS0016/RS0017 to protect.

ATProtoNet.Blazor's components are Razor-generated code, which this
analyzer (unlike CA2007) does not skip: their [Parameter] properties,
event callbacks and lifecycle overrides are genuinely part of the
package's public surface and are included in the baseline. The
generated BuildRenderTree override reports RS0041 ("oblivious
reference types") that an .editorconfig severity override cannot
reach (its diagnostics are keyed to the obj/ source-generator output,
not the .editorconfig hierarchy under the source tree), so it is
suppressed with <NoWarn> in the project file instead.

CONTRIBUTING.md gains a short "Public API tracking" section describing
the Shipped/Unshipped workflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
chore(src): compact comments (#139)
All checks were successful
CI / pds-integration (pull_request) Successful in 28s
CI / build-and-test (pull_request) Successful in 55s
50edefa3cc
Mechanical pass over src/ comments, no behavior change:

- Collapse a three-line /// <summary> block onto one line when its
  body is a single sentence that fits in ~120-130 chars (1,679 blocks
  across 275 files).
- Collapse three-line box-drawing section dividers into the one-line
  style already used elsewhere in the codebase, same total width
  (193 blocks across 41 files).
- Drop <param> tags that only restate the parameter's name in words
  (e.g. "Cancellation token." on `cancellationToken`, "The space." on
  `space`) - 683 tags across roughly 100 files. NoWarn CS1573 in
  src/Directory.Build.props, since dropping some but not all of a
  member's <param> tags would otherwise warn.

Grepped for history-style comments ("previously…", "used to be…",
"was changed…") and obvious-code narration: none were found:
the codebase already followed CLAUDE.md's existing comment
conventions before this pass. Nothing was reflowed or reworded.

Verified: every changed line in the src/ diff is a /// or // comment
line (or the new NoWarn entry's own explanatory comment); full
solution build is 0 warnings/0 errors; unit tests unaffected
(5045 passed, 5 skipped, run twice).

No CHANGELOG entry: comment-only, no user-visible or API effect.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Author
Owner

Mechanical-diff verification (orchestrator, scripted over git show -U0 of each commit after the rebase onto main):

  • ConfigureAwait (6f6281c), 70 .cs files. With whitespace normalised, .ConfigureAwait(false) stripped and #pragma lines ignored, every file's removed code equals its added code, so there are no other code changes.
  • Usings (448261c). Only using lines removed, nothing added, plus the IDE0005 line in src/.editorconfig.
  • Comments (50edefa). Every changed line in a .cs file is a // or /// line or blank. The only non-.cs change is NoWarn CS1573 in src/Directory.Build.props.
  • Public API (e43bd4e). Touches only PublicAPI.*.txt, Directory.Build.props, .editorconfig, the Blazor csproj (RS0041 NoWarn), CONTRIBUTING and CHANGELOG.
  • The build (--no-incremental) has 0 warnings and 0 errors, 5,058 unit tests pass, and dotnet pack produces all 6 packages.

@claude /review. The diff is huge but mechanical (see the verification above), so please don't try to read all of it. Focus on:

  1. src/.editorconfig and src/Directory.Build.props: the CA2007, IDE0005 and RS0016/RS0017 severities are scoped to src/ only and don't leak into tests or samples; PublicApiAnalyzers is PrivateAssets=all and absent from the nuspec.
  2. The 35 await using var x = await … sites that carry a #pragma warning disable CA2007 with a reason. Is each justified?
  3. ATProtoNet.Blazor: the Razor components are untouched, and AtProtoUserClientAccessor's ConfigureAwait(false) is safe because nothing after an await needs the circuit's context.
  4. Spot-check a sample of the compacted comments: no spec, security or concurrency rationale lost.
Mechanical-diff verification (orchestrator, scripted over `git show -U0` of each commit after the rebase onto main): - **ConfigureAwait (6f6281c)**, 70 .cs files. With whitespace normalised, `.ConfigureAwait(false)` stripped and `#pragma` lines ignored, every file's removed code equals its added code, so there are no other code changes. - **Usings (448261c).** Only `using` lines removed, nothing added, plus the IDE0005 line in `src/.editorconfig`. - **Comments (50edefa).** Every changed line in a .cs file is a `//` or `///` line or blank. The only non-.cs change is `NoWarn CS1573` in `src/Directory.Build.props`. - **Public API (e43bd4e).** Touches only `PublicAPI.*.txt`, `Directory.Build.props`, `.editorconfig`, the Blazor csproj (RS0041 NoWarn), CONTRIBUTING and CHANGELOG. - The build (`--no-incremental`) has 0 warnings and 0 errors, 5,058 unit tests pass, and `dotnet pack` produces all 6 packages. @claude /review. The diff is huge but mechanical (see the verification above), so please don't try to read all of it. Focus on: 1. `src/.editorconfig` and `src/Directory.Build.props`: the CA2007, IDE0005 and RS0016/RS0017 severities are scoped to `src/` only and don't leak into tests or samples; PublicApiAnalyzers is `PrivateAssets=all` and absent from the nuspec. 2. The 35 `await using var x = await …` sites that carry a `#pragma warning disable CA2007` with a reason. Is each justified? 3. `ATProtoNet.Blazor`: the Razor components are untouched, and `AtProtoUserClientAccessor`'s `ConfigureAwait(false)` is safe because nothing after an await needs the circuit's context. 4. Spot-check a sample of the compacted comments: no spec, security or concurrency rationale lost.
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-2026-09-26T23-03-28-174Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/629); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-629.md) — updating every ~5 minutes while the task runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-27T04:03:28.622Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-27T04:03:28.622Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f200a64c7ffeCjhPNe6scOe77x -->
claude-reviewer left a comment

💬 Verdict: comment — notes only, nothing blocking.

This is a large, mechanical, well-tested pass (ConfigureAwait, comment compaction, public API tracking, unused usings) and the diff is consistent with what the description promises: ConfigureAwait(false) is applied correctly (including the documented await using combined-statement workaround, which correctly configures only the explicit task await and leaves the implicit disposal await under a scoped #pragma warning disable/restore CA2007, so the declared type of the disposable is preserved), the dropped <param> tags all look like genuine restatement-of-the-name tautologies, and the PublicAPI.Shipped.txt additions look mechanically generated. No behavior changes are visible in the diff.

One real issue: CHANGELOG.md now has two near-duplicate bullets about the same [RequiresFact]/[RequiresTheory] enum-driven attribute change under ### Changed — the pre-existing one (tagged #137) and a new one this PR adds (also tagged #137, not #139) that just adds the [RequiresTheory(...)] mention. That entry describes work from a different, already-merged issue and doesn't belong to this PR's own change set; it reads like a stray artifact from a branch merge/rebase rather than an intentional edit. Worth squashing into the original bullet (or dropping) before merge, per this repo's own changelog-hygiene rule in CLAUDE.md.


⏱ 0m 57s · 1 turns · tokens: 82.2k in / 5.0k out (+23.9k cache-read)
📜 Full session transcript

💬 **Verdict: comment** — notes only, nothing blocking. This is a large, mechanical, well-tested pass (ConfigureAwait, comment compaction, public API tracking, unused usings) and the diff is consistent with what the description promises: ConfigureAwait(false) is applied correctly (including the documented `await using` combined-statement workaround, which correctly configures only the explicit task await and leaves the implicit disposal await under a scoped `#pragma warning disable/restore CA2007`, so the declared type of the disposable is preserved), the dropped `<param>` tags all look like genuine restatement-of-the-name tautologies, and the PublicAPI.Shipped.txt additions look mechanically generated. No behavior changes are visible in the diff. One real issue: `CHANGELOG.md` now has two near-duplicate bullets about the same `[RequiresFact]`/`[RequiresTheory]` enum-driven attribute change under `### Changed` — the pre-existing one (tagged `#137`) and a new one this PR adds (also tagged `#137`, not `#139`) that just adds the `[RequiresTheory(...)]` mention. That entry describes work from a different, already-merged issue and doesn't belong to this PR's own change set; it reads like a stray artifact from a branch merge/rebase rather than an intentional edit. Worth squashing into the original bullet (or dropping) before merge, per this repo's own changelog-hygiene rule in CLAUDE.md. --- ⏱ 0m 57s · 1 turns · tokens: 82.2k in / 5.0k out (+23.9k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-629.md) <!-- claude-verdict:comment --> <!-- claude-reviewed-head:50edefa3cc4fcb1c7bca80e6f58fa64fc4f85c64 -->
CHANGELOG.md Outdated
@ -329,6 +330,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **The client factory's per-user clients take `UserAgent` and `RateLimit` from `AddAtProto(o => …)`** — they were created with default options whatever the registration said. The other client options describe the registered client's own session and do not apply to them: each addresses its user's PDS and refreshes on demand under the factory's coordinator (#136)
- **One enum-driven `[RequiresFact]` gates every environment-dependent integration test** — `RequiresPdsFactAttribute`, `RequiresBlueskyFactAttribute`, `RequiresPdsAdminFactAttribute`, `RequiresJetstreamFactAttribute`, `RequiresJetstreamArchiveFactAttribute` and `RequiresSpacesFactAttribute` are replaced by `[RequiresFact(IntegrationRequirement.X)]`; `RequiresRedisFactAttribute` is unchanged. `ATPROTO_REQUIRE_INTEGRATION=1` now also covers the PDS and Bluesky requirements, which previously skipped silently instead of failing when their prerequisites were missing. Migration: replace the old attribute names with `[RequiresFact(IntegrationRequirement.<Name>)]` (#137)
- **`AuthenticatedClientFixture` no longer needs a pre-existing account** — like `SpaceNetworkFixture`, it now provisions and tears down its own throwaway account through the admin API, gated on `ATPROTO_PDS_ADMIN_PASSWORD` instead of `ATPROTO_TEST_HANDLE` / `ATPROTO_TEST_PASSWORD`. Setting both env vars still overrides it, signing in with that account instead. The CI PDS-integration job now also runs `AuthenticationTests`, `RepositoryTests`, `CustomRecordTests` and `IdentityResolutionTests` against the reference PDS on every PR, not just `PdsAdminTests` (#137)
- **One enum-driven `[RequiresFact]` / `[RequiresTheory]` gates every environment-dependent integration test** — `RequiresPdsFactAttribute`, `RequiresBlueskyFactAttribute`, `RequiresPdsAdminFactAttribute`, `RequiresJetstreamFactAttribute`, `RequiresJetstreamArchiveFactAttribute` and `RequiresSpacesFactAttribute` are replaced by `[RequiresFact(IntegrationRequirement.X)]` (and a `[RequiresTheory(...)]` counterpart); `RequiresRedisFactAttribute` is unchanged. `ATPROTO_REQUIRE_INTEGRATION=1` now also covers the PDS and Bluesky requirements, which previously skipped silently instead of failing when their prerequisites were missing. Migration: replace the old attribute names with `[RequiresFact(IntegrationRequirement.<Name>)]` (#137)
Collaborator

This bullet duplicates the existing [RequiresFact]/[RequiresTheory] entry a few lines above (also under ### Changed, tagged #137). Both describe the same attribute consolidation; this one only adds the [RequiresTheory(...)] mention and is tagged #137, not #139, even though it's being introduced by the #139 PR. This looks like a merge artifact rather than an intentional change belonging to this PR's scope (ConfigureAwait/comments/PublicAPI/usings) — worth merging into the original bullet or dropping.

This bullet duplicates the existing `[RequiresFact]`/`[RequiresTheory]` entry a few lines above (also under `### Changed`, tagged `#137`). Both describe the same attribute consolidation; this one only adds the `[RequiresTheory(...)]` mention and is tagged `#137`, not `#139`, even though it's being introduced by the #139 PR. This looks like a merge artifact rather than an intentional change belonging to this PR's scope (ConfigureAwait/comments/PublicAPI/usings) — worth merging into the original bullet or dropping.
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-2026-09-27T04-04-02-363Z.md) — updating every ~5 minutes while the review runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-27T09:03:59.073Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-27T09:03:59.073Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f1ef7395fffeFBQX2c9OwwtqzS -->
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-2026-09-27T09-04-32-663Z.md) — updating every ~5 minutes while the review runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-27T14:04:29.524Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-27T14:04:29.524Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f1de41a72ffe2ClLRSxd5OAPtt -->
docs(changelog): drop a #137 bullet duplicated by the rebase (#139)
All checks were successful
CI / pds-integration (pull_request) Successful in 43s
CI / build-and-test (pull_request) Successful in 1m30s
Sync Closures to GitHub / sync-closure (pull_request) Successful in 10s
6d552bba23
The rebase kept both the pre-#176 wording of the [RequiresFact] bullet and
the one #176 amended on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

@claude /review, please re-review. The duplicated #137 bullet you flagged was a rebase artifact: my CHANGELOG merge kept the pre-#176 wording alongside the version #176 amended. 6d552bb removes it, so the branch's [Unreleased] now differs from main only by this PR's own two bullets, which I checked with a script. The merge script now drops a stale copy of any bullet that upstream has edited. Nothing else changed since 50edefa.

@claude /review, please re-review. The duplicated #137 bullet you flagged was a rebase artifact: my CHANGELOG merge kept the pre-#176 wording alongside the version #176 amended. `6d552bb` removes it, so the branch's `[Unreleased]` now differs from main only by this PR's own two bullets, which I checked with a script. The merge script now drops a stale copy of any bullet that upstream has edited. Nothing else changed since 50edefa.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/630); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-630.md) — updating every ~5 minutes while the task runs.
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-2026-09-27T10-53-37-311Z.md) — updating every ~5 minutes while the review runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-27T15:53:37.817Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-27T15:53:37.817Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f1d8035bbffe62cbxcPGwdGK5w -->
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review confirms the one blocking issue from my last pass — the duplicate [RequiresFact]/[RequiresTheory] CHANGELOG bullet under ### Changed (a rebase artifact from #137/#176) — is now fixed by a dedicated follow-up commit (6d552bb, "docs(changelog): drop a #137 bullet duplicated by the rebase"), which removes the stray duplicate and keeps the single correct bullet plus this PR's own new ConfigureAwait bullet. I diffed that commit directly and confirmed the current CHANGELOG.md has no duplication left.

The remaining four commits (ConfigureAwait pass, comment compaction, PublicAPI tracking, unused usings) are unchanged from my prior pass, where I already verified: correct handling of the await using combined-statement CA2007 fixer bug (explicit task await configured, implicit disposal await left under a scoped #pragma warning disable/restore CA2007, preserving the disposable's declared type), genuine tautology-only <param> removals, and mechanically-generated PublicAPI.Shipped.txt additions. No behavior changes are visible in the diff, consistent with the PR's own claim of being purely mechanical.

No new issues found. Approving.


⏱ 0m 48s · 13 turns · tokens: 94.1k in / 3.2k out (+1337.6k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Re-review confirms the one blocking issue from my last pass — the duplicate `[RequiresFact]`/`[RequiresTheory]` CHANGELOG bullet under `### Changed` (a rebase artifact from #137/#176) — is now fixed by a dedicated follow-up commit (`6d552bb`, "docs(changelog): drop a #137 bullet duplicated by the rebase"), which removes the stray duplicate and keeps the single correct bullet plus this PR's own new `ConfigureAwait` bullet. I diffed that commit directly and confirmed the current CHANGELOG.md has no duplication left. The remaining four commits (ConfigureAwait pass, comment compaction, PublicAPI tracking, unused usings) are unchanged from my prior pass, where I already verified: correct handling of the `await using` combined-statement CA2007 fixer bug (explicit task await configured, implicit disposal await left under a scoped `#pragma warning disable/restore CA2007`, preserving the disposable's declared type), genuine tautology-only `<param>` removals, and mechanically-generated `PublicAPI.Shipped.txt` additions. No behavior changes are visible in the diff, consistent with the PR's own claim of being purely mechanical. No new issues found. Approving. --- ⏱ 0m 48s · 13 turns · tokens: 94.1k in / 3.2k out (+1337.6k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-177/run-630.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:6d552bba2307685f7efb7c3beed6b64844b0e316 -->
Grandiras deleted branch issue-139-chore 2026-09-27 10:54:56 +00:00
Sign in to join this conversation.
No description provided.