fix: bootstrap namespace self-ref and write access when created explicitly via --type namespace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-01 12:55:21 +02:00
parent 6288879468
commit 9e5194893e
2 changed files with 68 additions and 0 deletions

View File

@@ -943,4 +943,48 @@ func TestE2E(t *testing.T) {
}
})
})
t.Run("Namespace_ExplicitCreate", func(t *testing.T) {
nsDir, err := os.MkdirTemp("", "ax-ns-*")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(nsDir)
if err := exec.Command("cp", "./ax", filepath.Join(nsDir, "ax")).Run(); err != nil {
t.Fatal(err)
}
env := &testEnv{t: t, dir: nsDir, user: "testuser"}
env.mustAx("init")
// Explicitly create a namespace node via --type namespace.
nsNode := env.parseNode(env.mustAx("add", "myworkspace", "--type", "namespace", "--json"))
// The namespace should be in its own namespace (self-reference).
if !nsNode.HasRelation("in_namespace", nsNode.ID) {
t.Errorf("expected namespace to have in_namespace pointing to itself, got relations: %v", nsNode.Relations)
}
// The creator should have write access to the new namespace.
users := env.parseNodes(env.mustAx("list", "--type", "user", "--json"))
var userNode *NodeResponse
for i := range users {
if users[i].Title == "testuser" {
userNode = &users[i]
break
}
}
if userNode == nil {
t.Fatal("could not find testuser node")
}
if !userNode.HasRelation("has_write_access", nsNode.ID) {
t.Errorf("expected creator to have has_write_access to new namespace, got relations: %v", userNode.Relations)
}
// Nodes added to the new namespace should be accessible.
env.mustAx("add", "task in workspace", "--namespace", "myworkspace", "--json")
listed := env.parseNodes(env.mustAx("list", "--namespace", "myworkspace", "--json"))
if len(listed) == 0 {
t.Error("expected to list nodes in newly created namespace")
}
})
}

View File

@@ -328,6 +328,30 @@ func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
}
}
// Namespace bootstrap: when creating a namespace node directly, apply the
// same setup as ensureNamespace — self in_namespace and creator write access.
if tmp.GetProperty("type") == "namespace" {
if !hasNamespace {
// Replace the default namespace rel (user's ns) with self-reference.
userNsID, _ := s.resolveIDByNameAndType(st, s.userID, "namespace")
if userNsID != "" {
if err := st.RemoveRel(id, string(models.RelInNamespace), userNsID); err != nil {
return err
}
}
if err := st.AddRel(id, string(models.RelInNamespace), id); err != nil {
return err
}
}
creatorID, err := s.resolveUserRef(st, s.userID)
if err != nil {
return err
}
if err := st.AddRel(creatorID, string(models.RelHasWriteAccess), id); err != nil {
return err
}
}
return nil
})
if err != nil {