Skip to content

A surface says what it is, instead of borrowing a name that filtered right - #2137

Merged
rbuergi merged 3 commits into
mainfrom
feat/content-visibility
Aug 24, 2026
Merged

rbuergi merged 3 commits into
mainfrom
feat/content-visibility

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

The home screen listed the platform's own bookkeeping — every NodeType, ModuleDefinition, Partition and Role registration — next to the viewer's actual spaces. Those are in-memory declarations AddMeshNodes contributes so the platform knows a type exists. They are what a create menu offers. Nobody browses them.

The cause was a borrowed name

Every browsing surface passed context:search: the home catalog, the first-level union, a node's children list, the recent lists. None of them are the search box. They said it because search was the one context whose filtering withheld registrations — and each of them then still carried its own patch on top (-nodeType:NodeType, -nodeType:Store/Plugin), because a name you don't mean cannot say what you want.

What changed

Surfaces say what they are. New query term is:content, alongside the existing is:main. It withholds registration nodes, and it implies context:content — which is what keeps the existing exclusion machinery working unchanged: per-node ExcludeFromContext, and per-type when the marked node is a NodeType definition, pushed into SQL by the database providers. An explicit context: still wins.

Declaration belongs to whoever owns the thing, not to the surface. MeshNode carries the mark; MeshNodeVisibilityExtensions makes it one call:

// a module, next to its own registration
.AddMeshNodes(CreateRegistryCredentialNodeType().HideEverywhere())

// a deployment's final config, for something shipped by someone else
node.HideFromContent()

.HideFrom(...) / .HideFromContent() / .HideEverywhere() are additive and idempotent, so a module and a deployment can each contribute without silently dropping the other's. That is the whole point: a home screen that enumerates what to hide is wrong the moment anyone installs something it has not heard of.

The 52 sites that declared {"search"} / {"search","create"} now also declare "content". This is not new policy — it is the same nodes staying hidden from the same surfaces, said in the surfaces' own terms. Without it, dropping the borrowed context:search would have revealed them. (NodeTypeNodeType is the one that proves the point: the children list carried -nodeType:NodeType precisely because its {"search"} mark didn't cover that surface. That patch is now gone.)

Sites declaring only {"create"} (Release, Build) are deliberately untouched — they were always visible to browse and search, and still are.

The one list that survives

-nodeType:Store/Plugin -nodeType:Store/Catalog on the content scopes, with a comment saying why it is still there: those are dynamic node types living in the Store partition, so marking them is a MeshWeaver.Plugins change landing after this. Deleting the terms first would put every installed plugin's root back on the content list, duplicating its own app tile. Same for Hosting, which is a Store/Plugin instance.

Content tabs

All, then Pinned, Mine, Shared. Pinned and Shared stay conditional — a pin list with no pins and a path union with no paths both match nothing, so an always-present tab would be an always-empty one. Mine is unconditional: your own space is the one tab that is never empty for you. New home.mine key in both server catalogs and the clients/react mirror the drift guard checks.

Tests

ContentContextQueryTest (7) — is:content sets the flag and implies the context; an explicit context wins; a query that doesn't say it is untouched; HideFrom is additive and idempotent across two declarers. HomeTabsTest / PresentationSurfacesTest / HomeCatalogTest updated to the new tab set and the honest context name. Full MeshWeaver.Graph.Test: 1544 tests, green. Release -warnaserror clean across Mesh.Contract, Hosting, Graph, AI.

Follow-ups, not in this PR: marking Store/Plugin, Store/Catalog, Post and Event in the repos/mesh that own them (one .HideFromContent() each).

…right

The home screen listed the platform's own bookkeeping — every NodeType,
ModuleDefinition, Partition and Role registration — next to the viewer's actual
spaces. Those are in-memory declarations contributed by AddMeshNodes so the
platform knows a type exists. They are what a create menu offers. Nobody browses
them.

The cause was a borrowed name. Every browsing surface passed `context:search`:
the home catalog, the first-level union, a node's children list, the recent
lists. None of them are the search box. They said it because `search` was the one
context whose filtering withheld registrations — and each of them then STILL
carried its own patch on top (`-nodeType:NodeType`, `-nodeType:Store/Plugin`),
because a name you don't mean cannot say what you want.

So the surfaces now say what they are: `is:content`, alongside the existing
`is:main`. It withholds registrations, and it IMPLIES `context:content`, which is
what keeps the existing exclusion machinery working — per-node ExcludeFromContext
and, when the marked node is a NodeType definition, per-type, pushed into SQL by
the database providers. An explicit `context:` still wins.

Declaration belongs to whoever owns the thing, not to the surface: MeshNode
carries the mark, and MeshNodeVisibilityExtensions makes it one call —
`.HideFrom(...)`, `.HideFromContent()`, `.HideEverywhere()` — additive and
idempotent, so a module and a deployment can each contribute without silently
dropping the other's. That is the point: a home screen enumerating what to hide
is wrong the moment anyone installs something it has not heard of.

The 52 registration sites that declared {"search"} / {"search","create"} now also
declare "content". That is not new policy — it is the same nodes staying hidden
from the same surfaces, said in the surfaces' own terms. Without it, dropping the
borrowed `context:search` would have REVEALED them.

One list survives, and it is called out where it lives: `-nodeType:Store/Plugin
-nodeType:Store/Catalog` on the content scopes. Those are DYNAMIC node types
living in the Store partition, so marking them is a MeshWeaver.Plugins change
landing after this; deleting the terms first would put every installed plugin's
root back on the content list, duplicating its own app tile.

Content tabs, as asked: All, then Pinned, Mine, Shared. Pinned and Shared stay
conditional — a pin list with no pins and a path union with no paths both match
nothing, and an always-present tab would be an always-empty one. Mine is
unconditional: your own space is the one tab that is never empty for you.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 24, 2026 11:11
'let's just put Spaces for shared with me' — the tab answers 'what can I reach
that I don't own', and Spaces names that better than describing how it got
there. The key already existed in all four catalogs.

Pinned and Spaces stay conditional (no pins / no grants ⇒ no tab); Mine is the
viewer's own home path and is always there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

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

This PR introduces an explicit “content browsing” query intent (is:content) so UI browsing surfaces (home catalog, children lists, recent lists, etc.) stop pretending to be context:search just to inherit registration-node filtering. It also centralizes the context names (MeshContexts) and adds fluent helpers (MeshNodeVisibilityExtensions) so modules and deployments can declaratively hide nodes from specific surfaces.

Changes:

  • Add is:content parsing to QueryParser and thread it through ParsedQuery, defaulting Context to MeshContexts.Content when appropriate.
  • Update static-node querying and Graph browsing/home queries to use is:content (and adjust home tabs to include the new “Mine” tab).
  • Extend many registration/infrastructure node types’ ExcludeFromContext to include "content" and add localization keys for the new tab (server + React mirror).

Reviewed changes

Copilot reviewed 58 out of 58 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs Adds coverage for is:content parsing + visibility helpers.
test/MeshWeaver.Graph.Test/PresentationSurfacesTest.cs Updates expected content tab set to include “Mine”.
test/MeshWeaver.Graph.Test/HomeTabsTest.cs Updates home tab expectations and asserts is:content is used.
test/MeshWeaver.Graph.Test/HomeCatalogTest.cs Validates catalog queries use is:content instead of context:search.
src/MeshWeaver.PluginCatalog/PluginCatalogConfigurationExtensions.cs Hides plugin catalog bookkeeping types from "content" context.
src/MeshWeaver.Messaging.Hub/Localization/strings.en.json Adds home.mine (server catalog).
src/MeshWeaver.Messaging.Hub/Localization/strings.de.json Adds home.mine (server catalog).
src/MeshWeaver.Mesh.Contract/Query/QueryParser.cs Parses is:content and implies context:content when not explicitly set.
src/MeshWeaver.Mesh.Contract/Query/ParsedQuery.cs Adds IsContent to the parsed query contract.
src/MeshWeaver.Mesh.Contract/MeshNodeVisibilityExtensions.cs Adds fluent APIs to mark nodes as hidden from contexts.
src/MeshWeaver.Mesh.Contract/MeshContexts.cs Introduces named constants for query visibility contexts, including Content.
src/MeshWeaver.InstanceSync/InstanceSyncConfiguration.cs Hides instance-sync config nodes from "content" context.
src/MeshWeaver.Hosting/Persistence/Query/StaticNodeQueryProvider.cs Suppresses registration/config nodes when is:content is set.
src/MeshWeaver.Graph/UserActivityLayoutAreas.cs Switches home/catalog queries to is:content and adds “Mine” scope.
src/MeshWeaver.Graph/MeshNodeLayoutAreas.cs Switches children catalog query to is:content.
src/MeshWeaver.Graph/Configuration/VUserNodeType.cs Adds "content" exclusion for VUser.
src/MeshWeaver.Graph/Configuration/UserNodeType.cs Adds "content" exclusion for User.
src/MeshWeaver.Graph/Configuration/UserActivityNodeType.cs Adds "content" exclusion for UserActivity.
src/MeshWeaver.Graph/Configuration/TrackedChangeNodeType.cs Adds "content" exclusion for TrackedChange.
src/MeshWeaver.Graph/Configuration/TeamsConversationNodeType.cs Adds "content" exclusion for TeamsConversation.
src/MeshWeaver.Graph/Configuration/ScopeNodeType.cs Adds "content" exclusion for Scope.
src/MeshWeaver.Graph/Configuration/ScheduledActionNodeType.cs Adds "content" exclusion for ScheduledAction.
src/MeshWeaver.Graph/Configuration/RoleNodeType.cs Adds "content" exclusion for Role.
src/MeshWeaver.Graph/Configuration/PartitionNodeType.cs Adds "content" exclusion for Partition.
src/MeshWeaver.Graph/Configuration/PartitionAccessPolicyNodeType.cs Adds "content" exclusion for PartitionAccessPolicy.
src/MeshWeaver.Graph/Configuration/NotificationSettingsNodeType.cs Adds "content" exclusion for NotificationSettings.
src/MeshWeaver.Graph/Configuration/NotificationNodeType.cs Adds "content" exclusion for Notification.
src/MeshWeaver.Graph/Configuration/NodeTypeNodeType.cs Adds "content" exclusion for NodeType definitions.
src/MeshWeaver.Graph/Configuration/MeshWeaverInstanceNodeType.cs Adds "content" exclusion for instance bookkeeping nodes.
src/MeshWeaver.Graph/Configuration/MeshDataSourceNodeType.cs Adds "content" exclusion for MeshDataSource nodes.
src/MeshWeaver.Graph/Configuration/LicenseNodeType.cs Adds "content" exclusion for LicenseAcceptance.
src/MeshWeaver.Graph/Configuration/KernelNodeType.cs Adds "content" exclusion for KernelSession.
src/MeshWeaver.Graph/Configuration/InvitationNodeType.cs Adds "content" exclusion for Invitation.
src/MeshWeaver.Graph/Configuration/HomeConfigNodeType.cs Adds "content" exclusion for Home config nodes.
src/MeshWeaver.Graph/Configuration/GroupMembershipNodeType.cs Adds "content" exclusion for GroupMembership.
src/MeshWeaver.Graph/Configuration/GraphSubscriptionNodeType.cs Adds "content" exclusion for GraphSubscription.
src/MeshWeaver.Graph/Configuration/GlobalSettingsNodeType.cs Adds "content" exclusion for Global settings nodes.
src/MeshWeaver.Graph/Configuration/EventSubscriptionNodeType.cs Adds "content" exclusion for EventSubscription.
src/MeshWeaver.Graph/Configuration/EmailNodeType.cs Adds "content" exclusion for Email.
src/MeshWeaver.Graph/Configuration/EaCredentialNodeType.cs Adds "content" exclusion for EA Credential.
src/MeshWeaver.Graph/Configuration/CompletionMemoryNodeType.cs Adds "content" exclusion for CompletionMemory.
src/MeshWeaver.Graph/Configuration/CodeNodeType.cs Adds "content" exclusion for Code.
src/MeshWeaver.Graph/Configuration/AppNodeType.cs Adds "content" exclusion for Installed App nodes.
src/MeshWeaver.Graph/Configuration/ApiTokenNodeType.cs Adds "content" exclusion for ApiToken.
src/MeshWeaver.Graph/Configuration/ActivityNodeType.cs Adds "content" exclusion for Activity.
src/MeshWeaver.Graph/Configuration/ActivityLogSegmentNodeType.cs Adds "content" exclusion for ActivityLogSegment.
src/MeshWeaver.Graph/Configuration/AccessAssignmentNodeType.cs Adds "content" exclusion for AccessAssignment.
src/MeshWeaver.Graph/CommentNodeType.cs Adds "content" exclusion for Comment.
src/MeshWeaver.GitSync/GitHubSyncConfiguration.cs Adds "content" exclusion for GitSync bookkeeping nodes.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-24-content-visibility.md Adds a “What’s New” entry documenting the behavior change.
src/MeshWeaver.Blazor.Portal/PortalNodeType.cs Adds "content" exclusion for PortalSession.
src/MeshWeaver.AI/TokenUsage.cs Adds "content" exclusion for TokenUsage.
src/MeshWeaver.AI/ThreadComposerNodeType.cs Adds "content" exclusion for ThreadComposer.
src/MeshWeaver.AI/ModelTierNodeType.cs Adds "content" exclusion for ModelTierDefinition.
src/MeshWeaver.AI/ModelProviderNodeType.cs Adds "content" exclusion for model provider nodes.
src/MeshWeaver.AI/AiSettingsNodeType.cs Adds "content" exclusion for AI settings nodes.
clients/react/src/i18n/strings.en.json Adds home.mine to React mirror catalog.
clients/react/src/i18n/strings.de.json Adds home.mine to React mirror catalog.
Suppressed comments (7)

test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:35

  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.
    test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:42
  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.
    test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:52
  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.
    test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:60
  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.
    test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:74
  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.
    test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:85
  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.
    test/MeshWeaver.Hosting.Test/ContentContextQueryTest.cs:92
  • This test uses FluentAssertions (the .Should() extension) but MeshWeaver.Hosting.Test does not reference FluentAssertions, so this will not compile. Use xUnit Assert APIs instead.

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

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

    9 files  ±0      9 suites  ±0   3m 55s ⏱️ - 1m 4s
1 679 tests ±0  1 674 ✅ ±0  5 💤 ±0  0 ❌ ±0 
2 176 runs  +1  2 171 ✅ +1  5 💤 ±0  0 ❌ ±0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

♻️ This comment has been updated with latest results.

HideFromContent() takes no arguments — the example showed it taking node names,
which is exactly the kind of snippet someone pastes and then has to debug. It
now shows the real shape: one call, on the node.

The other review point on this PR is wrong: MeshWeaver.Hosting.Test does have
the .Should() assertions (global usings), and the file builds clean under
-c Release -warnaserror with all 7 of its tests green. No change there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

1 262 tests  ±0   1 262 ✅ ±0   15m 32s ⏱️ + 1m 34s
    9 suites ±0       0 💤 ±0 
    9 files   ±0       0 ❌ ±0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

   10 files  ±0     10 suites  ±0   7m 15s ⏱️ +10s
1 254 tests ±0  1 253 ✅ ±0  1 💤 ±0  0 ❌ ±0 
1 255 runs  ±0  1 254 ✅ ±0  1 💤 ±0  0 ❌ ±0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

2 140 tests  +14   1 841 ✅ +14   9m 56s ⏱️ + 1m 25s
    9 suites ± 0     299 💤 ± 0 
    9 files   ± 0       0 ❌ ± 0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

3 305 tests  +7   3 305 ✅ +7   8m 17s ⏱️ +33s
    8 suites ±0       0 💤 ±0 
    8 files   ±0       0 ❌ ±0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

This pull request removes 1 and adds 8 tests. Note that renamed tests count towards both.
MeshWeaver.Graph.Test.HomeTabsTest ‑ Content_NoPins_IsOneCategory_NoTabStrip
MeshWeaver.Graph.Test.HomeTabsTest ‑ Content_NoPins_LeadsWithAll_AndKeepsMine
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ A_query_that_does_not_say_is_content_is_untouched
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ An_explicit_context_always_wins_over_the_implied_one
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ HideEverywhere_covers_the_three_a_person_can_reach
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ HideFrom_is_additive_and_idempotent_so_two_declarers_cannot_drop_each_other
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ HideFrom_with_nothing_to_add_leaves_the_node_alone
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ Is_content_sets_the_flag_AND_implies_the_content_context
MeshWeaver.Hosting.Test.ContentContextQueryTest ‑ Is_main_still_parses_on_its_own

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 748 tests  ±0   1 748 ✅ ±0   9m 17s ⏱️ +15s
   10 suites ±0       0 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

    55 files  ± 0      55 suites  ±0   54m 14s ⏱️ + 2m 53s
11 388 tests +21  11 083 ✅ +21  305 💤 ±0  0 ❌ ±0 
11 886 runs  +22  11 581 ✅ +22  305 💤 ±0  0 ❌ ±0 

Results for commit 9d4d090. ± Comparison against base commit 10db821.

This pull request removes 1 and adds 22 tests. Note that renamed tests count towards both.
MeshWeaver.Graph.Test.HomeTabsTest ‑ Content_NoPins_IsOneCategory_NoTabStrip
MeshWeaver.Content.Test.ContributedParserPriorityTest ‑ BuiltInParsersStillHandleTheirOwnFormats
MeshWeaver.Content.Test.ContributedParserPriorityTest ‑ ContributedAgentParser_WinsOverTheCatchAllMarkdownParser
MeshWeaver.Content.Test.ContributedParserPriorityTest ‑ WithoutTheContributedParser_TheSameFileDegradesSilently
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ A_path_that_normalises_to_nothing_is_ANSWERED_not_thrown(path: \"   \")
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ A_path_that_normalises_to_nothing_is_ANSWERED_not_thrown(path: \"///\")
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ A_path_that_normalises_to_nothing_is_ANSWERED_not_thrown(path: \"/\")
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ A_path_that_normalises_to_nothing_is_ANSWERED_not_thrown(path: \"@/\")
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ A_path_that_normalises_to_nothing_is_ANSWERED_not_thrown(path: \"\")
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ A_path_that_normalises_to_nothing_is_ANSWERED_not_thrown(path: null)
MeshWeaver.Content.Test.ThreadMessageToolTest ‑ An_ownerless_top_level_thread_path_is_refused_with_the_reason
…

@rbuergi
rbuergi merged commit 3cdcad4 into main Aug 24, 2026
33 checks passed
@rbuergi
rbuergi deleted the feat/content-visibility branch August 28, 2026 08:56
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