Skip to content

Implement channel abstractions package#272

Merged
eanzhao merged 5 commits intodevfrom
feat/2026-04-21_issue-257-channel-abstractions
Apr 21, 2026
Merged

Implement channel abstractions package#272
eanzhao merged 5 commits intodevfrom
feat/2026-04-21_issue-257-channel-abstractions

Conversation

@eanzhao
Copy link
Copy Markdown
Contributor

@eanzhao eanzhao commented Apr 21, 2026

Closes #257

Summary

  • implement the initial Aevatar.GAgents.Channel.Abstractions surface for #257
  • add compile-time and CI enforcement for the single-class two-interfaces contract via a conformance suite and channel_mega_interface_guard.sh
  • inline the remaining interface contract semantics as XML docs and enable CS1591 as an error for the abstractions package
  • closes [Channel RFC] Core abstractions package (Aevatar.GAgents.Channel.Abstractions) #257

Notes

  • #256 is now merged into dev, so this PR carries only the #257 abstractions changes
  • proto comments now back the generated public API docs for the abstractions package
  • credential integration still reuses the foundation ICredentialProvider contract instead of introducing a channel-local duplicate

Validation

  • dotnet build agents/Aevatar.GAgents.Channel.Abstractions/Aevatar.GAgents.Channel.Abstractions.csproj --nologo
  • dotnet test test/Aevatar.GAgents.Channel.Protocol.Tests/Aevatar.GAgents.Channel.Protocol.Tests.csproj --nologo
  • bash tools/ci/architecture_guards.sh

@eanzhao eanzhao force-pushed the feat/2026-04-21_issue-257-channel-abstractions branch from 6032a94 to 3813d44 Compare April 21, 2026 05:10
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.59%. Comparing base (fad3bb6) to head (d6be7ab).
⚠️ Report is 6 commits behind head on dev.

@@           Coverage Diff           @@
##              dev     #272   +/-   ##
=======================================
  Coverage   68.58%   68.59%           
=======================================
  Files        1110     1110           
  Lines       78074    78074           
  Branches    10221    10221           
=======================================
+ Hits        53549    53551    +2     
+ Misses      20614    20612    -2     
  Partials     3911     3911           
Flag Coverage Δ
ci 68.59% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor Author

eanzhao commented Apr 21, 2026

重新 review 了最新修复,前一轮指出的代码问题都已经补齐:

  1. single-class two-interfaces 现在已经被真正编码进 gate。
  • ChannelAdapterConformanceSuite<TAdapter> 已经加上 where TAdapter : class, IChannelTransport, IChannelOutboundPort
  • tools/ci/channel_mega_interface_guard.sh 已加入 tools/ci/architecture_guards.sh
  1. no public API without doc 现在已经由构建强制。
  • Aevatar.GAgents.Channel.Abstractions.csproj 开启了 GenerateDocumentationFile=true
  • 同时把 CS1591 提升为 error
  1. XML doc 已经补到契约级别。
  • IChannelTransport / IChannelOutboundPort / ITurnContext 等接口现在已经把 lifecycle / auth / ordering / opaque id 等关键语义写进注释

我重新本地验证了:

  • dotnet build agents/Aevatar.GAgents.Channel.Abstractions/Aevatar.GAgents.Channel.Abstractions.csproj --nologo
  • dotnet test test/Aevatar.GAgents.Channel.Protocol.Tests/Aevatar.GAgents.Channel.Protocol.Tests.csproj --nologo
  • bash tools/ci/architecture_guards.sh

以上都通过。

如果按这次 review 明确略过 #257 acceptance 里“架构 review / sign-off”这一条流程项,仅从代码、文档和 gate 落地来看,我认为这个 PR 已经完成 #257 的承诺,可以在 PR 描述里写 closes #257

@eanzhao eanzhao marked this pull request as ready for review April 21, 2026 05:52
@eanzhao eanzhao marked this pull request as draft April 21, 2026 05:54
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36f3bec997

ℹ️ 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".

/// This helper always targets <see cref="Activity"/>'s conversation and uses the adapter's bot credential for the
/// current turn.
/// </remarks>
Task<EmitResult> SendAsync(MessageContent content);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add cancellation token to turn-scoped outbound calls

ITurnContext methods used for outbound I/O (send/reply/update/delete/stream start) do not accept a CancellationToken, so adapters are not contractually required to stop network operations when a turn is canceled (for example request abort, actor shutdown, or timeout). This can leave canceled turns still performing outbound writes and makes graceful shutdown semantics inconsistent across implementations.

Useful? React with 👍 / 👎.

/// </summary>
/// <param name="activityId">The opaque activity identifier returned by a previous send or reply.</param>
/// <returns>The emit result produced by the active adapter.</returns>
Task<EmitResult> DeleteAsync(string activityId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align delete result contract with outbound port semantics

ITurnContext.DeleteAsync returns EmitResult, but IChannelOutboundPort.DeleteAsync only returns Task, and EmitResult is documented as a send/update outcome type. This mismatch forces turn-context implementations to fabricate delete results (or hide failures behind exceptions only), which makes delete behavior observability inconsistent with the rest of the abstraction surface.

Useful? React with 👍 / 👎.

- Thread CancellationToken through ITurnContext send/reply/update/delete and BeginStreamingReplyAsync so adapters can honor turn cancellation (request abort, actor shutdown, timeout).
- Change ITurnContext.DeleteAsync to return Task to match IChannelOutboundPort.DeleteAsync; EmitResult.SentActivityId semantics do not apply to deletes, so implementations no longer need to fabricate emit results.
- Update docs/canon accordingly.

Addresses Codex P2 review comments on PR #272.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@eanzhao eanzhao marked this pull request as ready for review April 21, 2026 06:12
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 488cb9ea4d

ℹ️ 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".

Comment on lines +31 to +34
ArgumentNullException.ThrowIfNull(segments);

var normalized = new List<string> { NormalizeSegment(channel.Value, nameof(channel)) };
for (var i = 0; i < segments.Length; i++)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject empty canonical segment sets

BuildCanonicalKey accepts an empty segments array, and ConversationReference.Create exposes that path via optional params, so Create(channel, bot, scope, partition) currently produces a key with only the channel id. That collapses distinct conversations on the same channel onto the same canonical key and can break routing/dedup semantics once a caller omits segments by mistake; this helper should fail fast (or inject required identifiers) when no deterministic segments are provided.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d26dcbe1.

ConversationReference.BuildCanonicalKey(...) now rejects empty segments, and ConversationReference.Create(...) inherits that fail-fast behavior instead of allowing a channel-only canonical key. I also added a protocol test that asserts missing canonical segments throw immediately.

Comment on lines +13 to +14
public static RedactionResult Unchanged(byte[] payload) =>
new(payload ?? throw new ArgumentNullException(nameof(payload)), false);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defensive-copy payload bytes in redaction results

RedactionResult.Unchanged passes the caller’s byte[] through by reference, so if upstream uses pooled buffers or clears/reuses the array after redaction, SanitizedPayload mutates after the fact. That can lead to corrupted persisted payloads and can reintroduce sensitive data handling bugs in ingress flows; this contract should copy bytes (or switch to an immutable buffer type) before storing them.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d26dcbe1.

RedactionResult now defensively clones the supplied payload bytes before storing them, so later caller-side buffer reuse or mutation cannot change SanitizedPayload after the fact. The protocol tests now cover both Unchanged(...) and Modified(...) for this copy-on-store behavior.

Comment on lines +20 to +23
for path in files:
text = path.read_text(encoding="utf-8")
for match in pattern.finditer(text):
name = match.group(1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Aggregate interface parts in mega-interface guard

The guard evaluates each interface declaration body in isolation, so a partial interface split across files can place runtime methods in one part and outbound methods in another without ever tripping has_runtime_surface && has_outbound_surface. That bypasses the stated CI invariant while still producing a merged mega-interface at compile time; the check should aggregate detected methods by interface name before deciding violations.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d26dcbe1.

The guard now aggregates interface surface by interface name across all declaration parts before deciding violations, so a partial interface can no longer split runtime and outbound members across files to bypass the check. I also added a guard-focused test that creates a split partial interface fixture and verifies the script fails.

@eanzhao eanzhao merged commit 7cf53d4 into dev Apr 21, 2026
11 checks passed
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.

[Channel RFC] Core abstractions package (Aevatar.GAgents.Channel.Abstractions)

1 participant