Skip to content

Add member-first Studio APIs#427

Merged
eanzhao merged 2 commits intodevfrom
feat/2026-04-27_member-first-studio-apis
Apr 27, 2026
Merged

Add member-first Studio APIs#427
eanzhao merged 2 commits intodevfrom
feat/2026-04-27_member-first-studio-apis

Conversation

@eanzhao
Copy link
Copy Markdown
Contributor

@eanzhao eanzhao commented Apr 27, 2026

Problem

Studio Bind / Invoke / Observe needs member-first backend routes so the frontend can address a member and let the backend resolve the published service id.

Closes #364

Solution

  • Add IMemberPublishedServiceResolver and a deterministic default resolver that maps normalized memberId to the member-owned publishedServiceId while rejecting service-key-unsafe member ids.
  • Add member-first scope routes for published service resolution, binding upsert/status, invoke/stream invoke, run list/detail, audit, resume, signal, and stop.
  • Gate member binding writes to scope-admin until an actor-owned member catalog defines authoritative membership, ownership, and cleanup semantics.
  • Require member routes to pass both scope access and member access, allowing either the matching authenticated member or a scope-admin.
  • Remove public member API appId overrides and app/namespace exposure from member resolver/response contracts.
  • Keep observe/run reads on existing read-model-backed services and document the contract in docs/2026-04-27-member-first-studio-apis.md.

Impact Paths

  • src/Aevatar.Hosting
  • src/platform/Aevatar.GAgentService.Abstractions
  • src/platform/Aevatar.GAgentService.Application
  • src/platform/Aevatar.GAgentService.Hosting
  • test/Aevatar.GAgentService.Tests
  • test/Aevatar.GAgentService.Integration.Tests
  • test/Aevatar.Studio.Tests
  • docs/

Validation

  • PASS: dotnet test test/Aevatar.GAgentService.Tests/Aevatar.GAgentService.Tests.csproj --nologo --filter DefaultMemberPublishedServiceResolverTests
  • PASS: dotnet test test/Aevatar.Studio.Tests/Aevatar.Studio.Tests.csproj --nologo --filter ScopeEndpointAccessTests
  • PASS: dotnet test test/Aevatar.GAgentService.Integration.Tests/Aevatar.GAgentService.Integration.Tests.csproj --nologo --filter ScopeServiceEndpointsTests
  • PASS: dotnet test test/Aevatar.GAgentService.Tests/Aevatar.GAgentService.Tests.csproj --nologo
  • PASS: dotnet build aevatar.slnx --nologo
  • PASS: bash tools/ci/test_stability_guards.sh
  • PASS: bash tools/ci/query_projection_priming_guard.sh
  • PASS: git diff --check
  • PARTIAL: bash tools/ci/architecture_guards.sh passes through proto/channel/projection/workflow guards, then fails in playground_asset_drift_guard.sh because existing CLI playground build output differs from demos/Aevatar.Demos.Workflow.Web/wwwroot assets. This PR does not modify those frontend assets.

Copy link
Copy Markdown
Contributor Author

@eanzhao eanzhao left a comment

Choose a reason for hiding this comment

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

Reviewed against CLAUDE.md architecture rules and the linked #364 minimum slice. Tests + build pass and the slice is correctly scoped to unblock #340, but the new write surface introduces ownership/auth/validation concerns that should at minimum be tracked as known follow-ups before this becomes the long-lived contract — see inline comments.

Comment thread docs/2026-04-27-member-first-studio-apis.md Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

❌ Patch coverage is 74.71098% with 175 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.62%. Comparing base (8b2ce20) to head (783e2f4).
⚠️ Report is 3 commits behind head on dev.

Files with missing lines Patch % Lines
...Service.Hosting/Endpoints/ScopeServiceEndpoints.cs 73.19% 124 Missing and 28 partials ⚠️
src/Aevatar.Hosting/AevatarMemberAccessGuard.cs 77.22% 16 Missing and 7 partials ⚠️
@@            Coverage Diff             @@
##              dev     #427      +/-   ##
==========================================
+ Coverage   70.58%   70.62%   +0.03%     
==========================================
  Files        1176     1179       +3     
  Lines       84475    85165     +690     
  Branches    11127    11172      +45     
==========================================
+ Hits        59630    60148     +518     
- Misses      20539    20676     +137     
- Partials     4306     4341      +35     
Flag Coverage Δ
ci 70.62% <74.71%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...tractions/Ports/IMemberPublishedServiceResolver.cs 100.00% <100.00%> (ø)
.../Bindings/DefaultMemberPublishedServiceResolver.cs 100.00% <100.00%> (ø)
...DependencyInjection/ServiceCollectionExtensions.cs 96.81% <100.00%> (+0.02%) ⬆️
src/Aevatar.Hosting/AevatarMemberAccessGuard.cs 77.22% <77.22%> (ø)
...Service.Hosting/Endpoints/ScopeServiceEndpoints.cs 82.31% <73.19%> (-2.77%) ⬇️

... and 2 files 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.

@eanzhao eanzhao marked this pull request as ready for review April 27, 2026 03:55
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: 93b0c4f064

ℹ️ 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 thread src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeServiceEndpoints.cs Outdated
@eanzhao eanzhao force-pushed the feat/2026-04-27_member-first-studio-apis branch from 93b0c4f to 04e460e Compare April 27, 2026 04:05
Copy link
Copy Markdown
Contributor Author

@eanzhao eanzhao left a comment

Choose a reason for hiding this comment

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

All five comments addressed cleanly:

  1. Resource semantics gap// TODO added in resolver pointing to actor-owned catalog; PUT binding now requires scope-admin role via AevatarMemberAccessGuard.TryCreateScopeAdminRequiredResult. ✅
  2. memberId validationNormalizeMemberId rejects :, /, \, ?, #; new test ResolveAsync_ShouldRejectMemberIdThatBreaksServiceKeySegments covers it. ✅
  3. Authorization — New AevatarMemberAccessGuard enforces matching member claim or scope-admin role for reads/invoke, and scope-admin only for PUT binding; integration tests cover allow/deny paths via X-Test-Member-Id / X-Test-Role headers. ✅
  4. Run lifecycle ops — Added runs/{runId}/audit, :resume, :signal, :stop member endpoints with full integration tests; doc claim softened and table updated. ✅
  5. AppId / Namespace leakage — Removed AppId from request DTO, AppId/Namespace from response DTO, appId query param from all member endpoints; new UpsertMemberScopeBindingHttpRequest has clean public surface; doc adds explicit semantic. ✅

Resolver unit tests pass locally (3/3).

One minor observation, not blocking — feel free to fold into a follow-up: AevatarMemberAccessGuard.HasScopeAdminRole checks for any admin-typed role on the principal without validating it's bound to the requested scope. Today this is fine because AevatarScopeAccessGuard already enforces single-scope identity (rejects ambiguous scope claims), so admin-of-scope-A can't reach the member guard for scope-B. But if the system later supports multi-scope users, the role claim would need a scope binding (e.g., {type: "scope.role:scope-a", value: "admin"}) to avoid cross-scope admin escalation. Cheap to fix later when the actor-owned catalog lands.

@eanzhao eanzhao merged commit fb393b7 into dev Apr 27, 2026
12 checks passed
eanzhao added a commit that referenced this pull request Apr 27, 2026
dev added HandleInvokeMemberStreamAsync (PR #427 member-first APIs) which
forwards to HandleInvokeStreamAsync. Pass IServiceRunRegistrationPort
through so the new member-stream entry point also registers a service-run
record before delegating, matching the other stream entry points.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

[Blocker] Backend gaps blocking 100% pure member-first Invoke/Observe closeout for #340

1 participant