Skip to content

feat(social): Social rides the module lane — first endpoint-hook consumer - #1667

Merged
rbuergi merged 2 commits into
mainfrom
feat/social-module
Aug 16, 2026
Merged

feat(social): Social rides the module lane — first endpoint-hook consumer#1667
rbuergi merged 2 commits into
mainfrom
feat/social-module

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Migrates Social to the module lane as the first consumer of the endpoint-contribution hook (#1655). Stacked on #1663 (feat/1655-endpoint-hook) — the base retargets to main once that merges.

What moved (into src/MeshWeaver.Social)

  • EndpointsLinkedInConnectEndpoints, LinkedInPublishEndpoints, LinkedInPageSyncEndpoints left memex/Memex.Portal.Shared/Social/ and are now contributed by the assembly-level SocialModuleAttribute : MeshEndpointProviderAttribute, applied by the host's existing app.MapMeshModuleEndpoints(). Delisting the module 404s the routes wholesale.
  • Menu providersLinkedInCredentialMenuProvider + SocialPostMenuProvider. Verdict: both stay DI providers, not seeded UiContribution nodes — their predicates are beyond the closed vocabulary (live credential-presence query with self-hiding, viewer-own-User check, $type/platform/publishedUrn node-shape logic with a Publish↔Refresh swap). They register through the module's BuilderConfigurationsAddSocial(), and now gate on Social:LinkedIn:ClientId at runtime (previously the host registered them conditionally — behavior preserved, registration unconditional).
  • DI surfaceSocialMeshModuleAttribute : MeshNodeProviderAttribute folds AddSocial() (the OgCard two-lane rule: fixtures call the same public MeshBuilder.AddSocial() extension). LinkedInOptions now binds via AddOptions<T>().BindConfiguration("Social:LinkedIn") with a bare-instance singleton bridge so LinkedInPublisher's ctor (and its tests) keep the plain-options shape.

What stayed (deliberately)

  • ApiCredentialNodeType + .AddApiCredentialType() stay in Portal.Shared — the NodeType registration must survive delisting so existing credential nodes keep deserializing. The module carries only the NodeType name (PlatformCredential.ApiCredentialNodeType).
  • The LinkedIn SIGN-IN scheme split: AddLinkedInAuthentication(...) (the AddOAuth-based authentication scheme in the auth pipeline, MemexConfiguration service config) stays platform — auth schemes configure before the host builds. Only the ENDPOINTS moved. This is the auth-scheme/endpoint split called out in the design.
  • GitHubConnect/GitHubLogin/GitHubWebhook/InstanceConnect endpoints — untouched (auth-pipeline + different concern, scoped separately).

Auth semantics — route by route

The hook's group defaults to RequireAuthorization(). Every previously-authenticated route keeps its explicit RequireAuthorization(); the two routes that were registered without auth — the OAuth redirect targets /connect/linkedin/callback and /connect/linkedin/org/callback — opt out via .AllowAnonymous() with a comment: LinkedIn's redirect must not bounce through a login challenge (which would drop the code/state query); the CSRF state cookie set by the authenticated start route is the guard. Exact pre-move semantics, now pinned by test.

One hardening the pin test forced: DI parameters on the module's handlers are now explicit [FromServices] (+ [FromBody] on the JSON publish body). Minimal-API parameter inference classifies a service parameter by asking the host's container at endpoint-materialization time — a module must not depend on host registration to keep its parameters from degrading into inferred-body binding.

Activation / double-ship

  • MeshWeaver.Social.dll added to Modules:Assemblies of BOTH hosts (Monolith + Distributed appsettings). No deploy-side override of that list exists, so image-built portals pick it up.
  • Portal.Shared keeps its ProjectReference to MeshWeaver.Social (ships-the-bits double-ship state); memex/MeshModulesPublish.targets gains the module (thin lane); MeshWeaver.slnx already carried the project (verified).
  • Portal.Shared: 5 files deleted; the Social service block and the three MapLinkedIn* calls removed from MemexConfiguration.
  • MeshWeaver.Social.csproj gains FrameworkReference Microsoft.AspNetCore.App + refs to MeshWeaver.Hosting.AspNetCore (the hook) and MeshWeaver.Graph (menu-provider surface).

Docs

Doc/Architecture/Modules inventory gains the MeshWeaver.Social.dll row (Social:LinkedIn config section) + a first-consumer note; Doc/DataMesh/SocialMedia/LinkedInPublishing location paragraph updated; What's New entry included.

Verification

  • Release -warnaserror clean, one project per invocation: MeshWeaver.Social, Memex.Portal.Shared, Memex.Portal.Monolith, Memex.Portal.Distributed, MeshWeaver.Hosting.Monolith.Test, MeshWeaver.Social.Test, Memex.Portal.Shared.Test, MeshWeaver.Documentation.Test.
  • New pin test SocialModuleContributionTest (sibling of ModuleEndpointContributionTest): both attributes present with non-empty contributions; installing the assembly applies AddSocial (providers scoped on INodeMenuProvider, options + typed client registered); all 9 routes map through the hook with the exact auth semantics (only the two callbacks anonymous). Passed! 10/10 with the hook + LinkedIn publish tests.
  • Full MeshWeaver.Social.Test (33/33) and full Memex.Portal.Shared.Test green; DocumentationLinkIntegrityTest green.
  • Live-mesh sweep (memex MCP): no in-mesh Source/NodeType code references the moved compiled symbols or the old Memex.Portal.Shared.Social namespace (checked LinkedIn/LinkedInProfile/Source/*, SocialMedia/Post/Source/*, Code-node search); route URLs referenced from mesh content are unchanged.

Notes / deviations

  • LinkedInCredentialMenuProvider's "Download past posts" item links /connect/linkedin/pullno compiled endpoint maps that route anywhere in the repo (pre-existing; unchanged by this move). Flagging for a follow-up.
  • Touched-code cleanups per repo rules: the page-sync credential read and the menu provider's platform probe now use ContentAs<PlatformCredential> instead of direct casts.

🤖 Generated with Claude Code

…umer

MeshWeaver.Social grows into a full module: the LinkedIn connect/publish/
page-sync endpoints move out of Memex.Portal.Shared as SocialModuleAttribute
(MeshEndpointProviderAttribute) contributions applied by the host's
MapMeshModuleEndpoints, and the DI surface (LinkedInOptions via the options
pipeline, the LinkedInPublisher typed client, both node-menu providers) rides
SocialMeshModuleAttribute -> AddSocial() — one configure path for the
attribute lane and explicit composition (the OgCard two-lane rule).

The two OAuth redirect targets keep their pre-move anonymous semantics via
explicit AllowAnonymous (the CSRF state cookie is the guard); every other
route stays authenticated under the hook's group default. DI parameters on
the module's handlers are explicit [FromServices] so a module endpoint never
depends on host-container inference for parameter classification.

Stays platform: ApiCredentialNodeType (existing credential nodes must
deserialize when the module is delisted) and the LinkedIn SIGN-IN scheme
(AddLinkedInAuthentication — auth schemes configure before the host builds).

Activation: MeshWeaver.Social.dll in both hosts' Modules:Assemblies, the
modules/ publish layout, double-ship ProjectReference kept. Pinned by
SocialModuleContributionTest (both attributes, AddSocial registration shape,
all 9 routes with exact auth semantics).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 0)

918 tests  ±0   917 ✅ ±0   10m 4s ⏱️ - 1m 6s
 10 suites ±0     1 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 5)

1 371 tests  ±0   1 370 ✅ ±0   5m 6s ⏱️ - 1m 7s
   11 suites ±0       1 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 4)

1 542 tests  ±0   1 536 ✅ ±0   7m 6s ⏱️ -49s
   11 suites ±0       6 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 3)

   11 files  ±0     11 suites  ±0   5m 55s ⏱️ -26s
2 196 tests ±0  2 005 ✅ ±0  191 💤 ±0  0 ❌ ±0 
2 558 runs  +1  2 367 ✅ +1  191 💤 ±0  0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

♻️ This comment has been updated with latest results.

@rbuergi
rbuergi changed the base branch from feat/1655-endpoint-hook to main August 16, 2026 10:03
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 2)

2 320 tests   - 2   2 316 ✅  - 2   7m 11s ⏱️ +13s
   11 suites ±0       4 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

This pull request removes 59 and adds 57 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.SourceDiscoveryUnavailableTest ‑ StarvedSourceDiscovery_IsNotACompileVerdict_WhileARealCompileErrorStillIs
MeshWeaver.Hosting.Monolith.Test.SpaceCreateAtomicityTest ‑ FailedCreatorGrant_LeavesNoOrphanGrantEither
MeshWeaver.Hosting.Monolith.Test.SpaceCreateAtomicityTest ‑ FailedCreatorGrant_RollsBackTheRow_AndTheCreateStaysRetryable
MeshWeaver.Hosting.Monolith.Test.SpaceEditableTitleTest ‑ SpaceOverview_RendersClickToEditTitle_ForEditor
MeshWeaver.Hosting.Monolith.Test.SpaceWelcomeTest ‑ Space_HasBodyProperty_ForUserEditableMarkdown
MeshWeaver.Hosting.Monolith.Test.SpaceWelcomeTest ‑ WelcomeMarkdown_InvitesUserToPersonalizeAndChat
MeshWeaver.Hosting.Monolith.Test.SpaceWelcomeTest ‑ WelcomeMarkdown_IsPlainMarkdown_NoInlineStyledDivs
MeshWeaver.Hosting.Monolith.Test.StaleActivationSeedRollbackTest ‑ PostRecycleWrite_LandsAboveTheDurableVersion_AndDoesNotRollTheStoreBack
MeshWeaver.Hosting.Monolith.Test.StaleActivationSeedRollbackTest ‑ ReactivatedOwner_SeedsFromDurableStorage_NotFromTheRoutingCache
MeshWeaver.Hosting.Monolith.Test.StarvedPermissionReadTest ‑ StarvedPermissionRead_AnswersUnavailable_WhileAHealthyScopeStillCreates
…
MeshWeaver.Hosting.Monolith.Test.SocialModuleContributionTest ‑ InstallingTheAssembly_AppliesAddSocial
MeshWeaver.Hosting.Monolith.Test.SocialModuleContributionTest ‑ SocialEndpoints_MapThroughTheHook_WithTheExactAuthSemantics
MeshWeaver.Hosting.Monolith.Test.SocialModuleContributionTest ‑ TheAssembly_CarriesBothModuleAttributes_WithNonEmptyContributions
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ EveryOtherCompileFailure_StillStampsError
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ FailedProbe_DoesNotVeto_AHealthyCachedAnswerThatArrivesLater
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ FailedSourceQuery_ReportsUnestablished_NeverItsPartialRemainder
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ TheThreeCases_GateDifferently
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ UnestablishedSourceSet_StampsUnavailable_NeverError
MeshWeaver.Hosting.Monolith.Test.SpaceEditMarkdownTest ‑ SpaceEdit_MarkdownBackedSpace_LoadsContentIntoStandardEditor
MeshWeaver.Hosting.Monolith.Test.SpaceEditMarkdownTest ‑ SpaceEdit_RendersMarkdownEditorOnBody_NotPropertyForm
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 1)

2 247 tests  +5   2 144 ✅ +5   7m 40s ⏱️ +23s
   11 suites ±0     103 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

This pull request removes 54 and adds 59 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ EveryOtherCompileFailure_StillStampsError
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ FailedProbe_DoesNotVeto_AHealthyCachedAnswerThatArrivesLater
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ FailedSourceQuery_ReportsUnestablished_NeverItsPartialRemainder
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ TheThreeCases_GateDifferently
MeshWeaver.Hosting.Monolith.Test.SourceSnapshotEstablishmentTest ‑ UnestablishedSourceSet_StampsUnavailable_NeverError
MeshWeaver.Hosting.Monolith.Test.SpaceEditMarkdownTest ‑ SpaceEdit_MarkdownBackedSpace_LoadsContentIntoStandardEditor
MeshWeaver.Hosting.Monolith.Test.SpaceEditMarkdownTest ‑ SpaceEdit_RendersMarkdownEditorOnBody_NotPropertyForm
MeshWeaver.Hosting.Monolith.Test.SpaceOverviewBodyTest ‑ SpaceOverview_RendersContentBody_NotWelcomeTemplate
MeshWeaver.Hosting.Monolith.Test.StaleActivationDurableFirstSeedTest ‑ LiveHub_AdoptingAnExternalDurableWrite_MintsNothing_AndWritesNothingBack
MeshWeaver.Hosting.Monolith.Test.StaleActivationDurableFirstSeedTest ‑ PostRecycleWrite_WhenDurableSeedLagsTheRoutingCache_LandsAboveDurableTruth
…
MeshWeaver.Hosting.Monolith.Test.SourceDiscoveryUnavailableTest ‑ StarvedSourceDiscovery_IsNotACompileVerdict_WhileARealCompileErrorStillIs
MeshWeaver.Hosting.Monolith.Test.SpaceCreateAtomicityTest ‑ FailedCreatorGrant_LeavesNoOrphanGrantEither
MeshWeaver.Hosting.Monolith.Test.SpaceCreateAtomicityTest ‑ FailedCreatorGrant_RollsBackTheRow_AndTheCreateStaysRetryable
MeshWeaver.Hosting.Monolith.Test.SpaceEditableTitleTest ‑ SpaceOverview_RendersClickToEditTitle_ForEditor
MeshWeaver.Hosting.Monolith.Test.SpaceWelcomeTest ‑ Space_HasBodyProperty_ForUserEditableMarkdown
MeshWeaver.Hosting.Monolith.Test.SpaceWelcomeTest ‑ WelcomeMarkdown_InvitesUserToPersonalizeAndChat
MeshWeaver.Hosting.Monolith.Test.SpaceWelcomeTest ‑ WelcomeMarkdown_IsPlainMarkdown_NoInlineStyledDivs
MeshWeaver.Hosting.Monolith.Test.StaleActivationSeedRollbackTest ‑ PostRecycleWrite_LandsAboveTheDurableVersion_AndDoesNotRollTheStoreBack
MeshWeaver.Hosting.Monolith.Test.StaleActivationSeedRollbackTest ‑ ReactivatedOwner_SeedsFromDurableStorage_NotFromTheRoutingCache
MeshWeaver.Hosting.Monolith.Test.StarvedPermissionReadTest ‑ StarvedPermissionRead_AnswersUnavailable_WhileAHealthyScopeStillCreates
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results

    65 files  ±0      65 suites  ±0   43m 5s ⏱️ - 2m 50s
10 594 tests +3  10 288 ✅ +3  306 💤 ±0  0 ❌ ±0 
10 956 runs  +4  10 650 ✅ +4  306 💤 ±0  0 ❌ ±0 

Results for commit 2408f9c. ± Comparison against base commit 0a8f869.

♻️ This comment has been updated with latest results.

# Conflicts:
#	memex/Memex.Portal.Monolith/appsettings.json
#	memex/MeshModulesPublish.targets
#	memex/aspire/Memex.Portal.Distributed/appsettings.json
Copilot AI lite review requested due to automatic review settings August 16, 2026 10:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Migrates the LinkedIn Social feature from portal-compiled wiring to the “module lane” as the first concrete consumer of the new endpoint-contribution hook, so enabling/disabling MeshWeaver.Social.dll cleanly adds/removes its HTTP routes and UI menu actions.

Changes:

  • Adds Social module assembly attributes to contribute endpoints via MapMeshModuleEndpoints() and register Social DI via the module install path.
  • Updates LinkedIn connect/publish/page-sync endpoints and node-menu providers to live under MeshWeaver.Social and to use explicit minimal-API binding annotations.
  • Adds a pinning test plus documentation/What’s New updates and includes the Social module in Memex module publish + host module lists.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/MeshWeaver.Hosting.Monolith.Test/SocialModuleContributionTest.cs Adds a pin test asserting Social contributes endpoints + DI registrations and that auth semantics match expectations.
test/MeshWeaver.Hosting.Monolith.Test/MeshWeaver.Hosting.Monolith.Test.csproj References MeshWeaver.Social for the new monolith-hosting test.
src/MeshWeaver.Social/SocialPostMenuProvider.cs Moves/adjusts post-node menu provider and gates it on LinkedIn configuration.
src/MeshWeaver.Social/SocialModuleAttribute.cs Introduces assembly-level module attributes and a single AddSocial() registration surface.
src/MeshWeaver.Social/PlatformCredential.cs Adds a shared constant for the ApiCredential NodeType name (registration remains host-side).
src/MeshWeaver.Social/MeshWeaver.Social.csproj Adds ASP.NET framework reference and references hosting hook + Graph for menu-provider surface.
src/MeshWeaver.Social/LinkedInPublishService.cs Updates docs/comment reference to the moved endpoints.
src/MeshWeaver.Social/LinkedInPublisher.cs Adds a SectionName constant for options binding.
src/MeshWeaver.Social/LinkedInPublishEndpoints.cs Updates namespace and makes service/body binding explicit for module-contributed endpoints.
src/MeshWeaver.Social/LinkedInPageSyncEndpoints.cs Updates namespace, uses ContentAs, adds explicit binding and pins anonymous callback semantics.
src/MeshWeaver.Social/LinkedInCredentialMenuProvider.cs Updates namespace, uses ContentAs, gates on options, and simplifies platform extraction.
src/MeshWeaver.Social/LinkedInConnectEndpoints.cs Updates namespace, adds explicit binding and pins anonymous callback semantics.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-16-social-module.md Adds a What’s New entry describing the module-lane move.
src/MeshWeaver.Documentation/Data/DataMesh/SocialMedia/LinkedInPublishing.md Updates documentation to reflect endpoints + menu actions living in the Social module.
src/MeshWeaver.Documentation/Data/Architecture/Modules.md Documents Social as first endpoint-hook consumer and adds it to module inventory table.
memex/MeshModulesPublish.targets Adds Social module project to module publish list.
memex/Memex.Portal.Shared/Social/GitHubConnectEndpoints.cs Updates doc reference to point at the module-provided LinkedIn connect endpoints.
memex/Memex.Portal.Shared/MemexConfiguration.cs Removes portal-compiled Social wiring and documents what remains platform-owned (auth scheme + NodeType registration).
memex/Memex.Portal.Monolith/appsettings.json Adds MeshWeaver.Social.dll to Modules:Assemblies.
memex/aspire/Memex.Portal.Distributed/appsettings.json Adds MeshWeaver.Social.dll to Modules:Assemblies.
Suppressed comments (3)

src/MeshWeaver.Social/SocialPostMenuProvider.cs:35

  • INodeMenuProvider.Context is consistently set via NodeMenuItemsExtensions.NodeMenuContext (e.g. src/MeshWeaver.Graph/Configuration/NodeMenuItemsExtensions.cs:36) to avoid typos and keep context names centralized. This provider uses the literal string instead.
    src/MeshWeaver.Social/LinkedInPageSyncEndpoints.cs:110
  • The org callback handler later assumes Social:LinkedIn:ClientId is configured (null-forgiving config[...]!). Adding a defensive check near the top avoids a possible exception if configuration changes between start/callback or the callback is hit directly.
    src/MeshWeaver.Social/LinkedInConnectEndpoints.cs:118
  • The callback handler later assumes Social:LinkedIn:ClientId is configured (null-forgiving config[...]!). Adding a defensive check near the top avoids a possible exception if configuration changes between start/callback or the callback is hit directly.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@rbuergi
rbuergi merged commit cd78c6c into main Aug 16, 2026
23 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.

2 participants