Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/MeshWeaver.AI/BuiltInAgentProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
// The write caps keep the built-in agents unmodifiable.
yield return new MeshNode("_Policy", RootNamespace)
{
// 🚨 A satellite must point at its MAIN node, not at itself (#2383): the plain
// constructor defaults MainNode to the node's own path, which makes _Policy a main
// node by the catalog's definition (is:main == MainNode != Path) and lists "Access
// Policy" as content on every cover. New satellites: MeshNode.Satellite(id, main).
MainNode = RootNamespace,
NodeType = "PartitionAccessPolicy",
Name = "Access Policy",
Content = new PartitionAccessPolicy
Expand Down
5 changes: 5 additions & 0 deletions src/MeshWeaver.AI/BuiltInHarnessProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
// World-readable catalog, unmodifiable — same shape as the agent catalog.
yield return new MeshNode("_Policy", HarnessNodeType.RootNamespace)
{
// 🚨 A satellite must point at its MAIN node, not at itself (#2383): the plain
// constructor defaults MainNode to the node's own path, which makes _Policy a main
// node by the catalog's definition (is:main == MainNode != Path) and lists "Access
// Policy" as content on every cover. New satellites: MeshNode.Satellite(id, main).
MainNode = HarnessNodeType.RootNamespace,
NodeType = "PartitionAccessPolicy",
Name = "Access Policy",
Content = new PartitionAccessPolicy
Expand Down
10 changes: 10 additions & 0 deletions src/MeshWeaver.AI/BuiltInLanguageModelProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
// ALWAYS seed the read-only access policy for the catalog partition.
yield return new MeshNode("_Policy", ModelProviderNodeType.RootNamespace)
{
// 🚨 A satellite must point at its MAIN node, not at itself (#2383): the plain
// constructor defaults MainNode to the node's own path, which makes _Policy a main
// node by the catalog's definition (is:main == MainNode != Path) and lists "Access
// Policy" as content on every cover. New satellites: MeshNode.Satellite(id, main).
MainNode = ModelProviderNodeType.RootNamespace,
NodeType = "PartitionAccessPolicy",
Name = "Access Policy",
Content = new PartitionAccessPolicy
Expand Down Expand Up @@ -321,6 +326,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
if (!string.Equals(LanguageModelNodeType.RootNamespace, ModelProviderNodeType.RootNamespace, StringComparison.Ordinal))
yield return new MeshNode("_Policy", LanguageModelNodeType.RootNamespace)
{
// 🚨 A satellite must point at its MAIN node, not at itself (#2383): the plain
// constructor defaults MainNode to the node's own path, which makes _Policy a main
// node by the catalog's definition (is:main == MainNode != Path) and lists "Access
// Policy" as content on every cover. New satellites: MeshNode.Satellite(id, main).
MainNode = LanguageModelNodeType.RootNamespace,
NodeType = "PartitionAccessPolicy",
Name = "Access Policy",
Content = new PartitionAccessPolicy
Expand Down
5 changes: 5 additions & 0 deletions src/MeshWeaver.AI/BuiltInSkillProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
// (the Harness wedge, prod 2026-06-15). The write caps keep the built-in skills unmodifiable.
yield return new MeshNode("_Policy", SkillNodeType.RootNamespace)
{
// 🚨 A satellite must point at its MAIN node, not at itself (#2383): the plain
// constructor defaults MainNode to the node's own path, which makes _Policy a main
// node by the catalog's definition (is:main == MainNode != Path) and lists "Access
// Policy" as content on every cover. New satellites: MeshNode.Satellite(id, main).
MainNode = SkillNodeType.RootNamespace,
NodeType = "PartitionAccessPolicy",
Name = "Access Policy",
Content = new PartitionAccessPolicy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
Name: Access Policy no longer appears as page content
Category: Fix
Description: The built-in Agent, Skill, Model, Harness and Documentation covers stop listing their internal access-policy node under Contents.
Icon: DocumentBulletList
Order: -20260826
---

# Access Policy no longer appears as page content

The contents section of the built-in Agent, Skill, Model, Harness and Documentation pages no longer shows an "Access Policy" entry. That entry is an internal security setting, not content, and it was appearing because it had been created without saying which page it belongs to.
5 changes: 5 additions & 0 deletions src/MeshWeaver.Documentation/DocumentationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public static TBuilder AddDocumentation<TBuilder>(this TBuilder builder,
builder.AddMeshNodes(
new MeshNode("_Policy", DocumentationNodeProvider.RootNamespace)
{
// 🚨 A satellite must point at its MAIN node, not at itself (#2383): the plain
// constructor defaults MainNode to the node's own path, which makes _Policy a main
// node by the catalog's definition (is:main == MainNode != Path) and lists "Access
// Policy" as content on every cover. New satellites: MeshNode.Satellite(id, main).
MainNode = DocumentationNodeProvider.RootNamespace,
NodeType = "PartitionAccessPolicy",
Name = "Documentation Access Policy",
Content = new PartitionAccessPolicy
Expand Down
14 changes: 14 additions & 0 deletions src/MeshWeaver.Mesh.Contract/MeshNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ public record MeshNode([property: Key] string Id, [property: Editable(false)] st
[Editable(false)]
public string MainNode { get; init; } = string.IsNullOrEmpty(Namespace) ? Id : $"{Namespace}/{Id}";

/// <summary>
/// A SATELLITE of the node at <paramref name="mainNode"/>: created with <see cref="MainNode"/>
/// already pointing at its primary, which is what the doc on <see cref="MainNode"/> promises and
/// what every listing relies on to keep satellites out of a node's contents (the catalog's
/// <c>is:main</c> is literally <c>MainNode != Path</c>). 🚨 Minting a satellite with
/// <c>new MeshNode(id, ns)</c> leaves MainNode defaulting to ITSELF, which makes it a main node
/// by definition — that is how <c>Access Policy</c> came to be listed as content on every
/// package cover (#2383). Use this for <c>_Policy</c> and every other underscore-prefixed
/// SIBLING node. (Access assignments are not siblings — they live under a <c>_Access/</c>
/// namespace, e.g. <c>Doc/_Access/{id}</c> — so they are not what this factory is for.)
/// </summary>
public static MeshNode Satellite(string id, string mainNode) =>
new(id, mainNode) { MainNode = mainNode };

/// <summary>
/// Single segments as used for matching and addressing.
/// Must be a computed property (not a readonly field) so that it reflects
Expand Down
24 changes: 24 additions & 0 deletions test/MeshWeaver.Data.Test/MeshNodeSatelliteTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using MeshWeaver.Mesh;
using Xunit;

namespace MeshWeaver.Data.Test;

public class MeshNodeSatelliteTest
{
[Fact]
public void A_satellite_points_at_its_main_node_not_at_itself()
{
var policy = MeshNode.Satellite("_Policy", "Teams");
Assert.Equal("Teams/_Policy", policy.Path);
Assert.Equal("Teams", policy.MainNode);
Assert.NotEqual(policy.Path, policy.MainNode); // the catalog's is:main predicate
}

/// <summary>The trap this factory exists to close: the plain constructor makes a MAIN node.</summary>
[Fact]
public void The_plain_constructor_defaults_MainNode_to_itself()
{
var node = new MeshNode("_Policy", "Teams");
Assert.Equal(node.Path, node.MainNode);
}
}
Loading