From 7a169f82e05f7ab5beb631a31d4d536e010cf3de Mon Sep 17 00:00:00 2001 From: "Jose I. Paris" Date: Tue, 22 Sep 2026 17:54:33 +0200 Subject: [PATCH] fix(cli): tell the two trace init project dead ends apart (PFM-7409) A caller that can neither create a project nor write to one got a single message blaming the projects it can see for not accepting a workflow. In an organization the caller has no project membership in there are no such projects, so the message described neither what happened nor what to ask an administrator for. Split it into the missing-membership case and the read-only-projects case, and drop the workflow wording, which named an internal permission rather than anything asked for at that point in the wizard. Assisted-by: Claude Code Signed-off-by: Jose I. Paris Chainloop-Trace-Sessions: 09fcf4a8-b750-4b57-9048-a9fdb2c3d546 --- app/cli/cmd/trace_init_identity.go | 10 ++++++++-- app/cli/cmd/trace_init_identity_test.go | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/cli/cmd/trace_init_identity.go b/app/cli/cmd/trace_init_identity.go index ee5ceeadd..725c3ec64 100644 --- a/app/cli/cmd/trace_init_identity.go +++ b/app/cli/cmd/trace_init_identity.go @@ -357,9 +357,15 @@ func resolveInteractiveProject(ctx context.Context, lister projectLister, p prom // Creating one is a permission of the organization role, not of any project: // an organization contributor administering every project it can see still // cannot create another. With nothing to pick either, there is no way - // forward, so say so instead of asking. + // forward, so say so instead of asking. Seeing no project at all and seeing + // only projects that cannot be written to are different dead ends, and what + // there is to ask an administrator for differs with them. if !listing.CanCreateProject && len(writable) == 0 { - return nil, errors.New("you cannot create projects in this organization, and none of the projects you can see accepts a new workflow; ask an administrator for access to one") + if len(readOnly) == 0 { + return nil, errors.New("you do not belong to any project in this organization, and cannot create a new one; ask an administrator to add you to one") + } + + return nil, errors.New("you can only view the projects in this organization, and cannot create a new one; ask an administrator for write access to one") } // Nothing to choose from, so go straight to naming one rather than showing a diff --git a/app/cli/cmd/trace_init_identity_test.go b/app/cli/cmd/trace_init_identity_test.go index 42274fcda..2e2603316 100644 --- a/app/cli/cmd/trace_init_identity_test.go +++ b/app/cli/cmd/trace_init_identity_test.go @@ -685,12 +685,23 @@ func TestResolveInteractiveProject(t *testing.T) { }, { // Nothing to pick and nothing to create is a dead end, so it says so - // instead of asking for a name the control plane would refuse. - name: "no writable project and no permission to create says so", + // instead of asking for a name the control plane would refuse. The + // projects are there to be seen, so the reason is the missing write + // access, not a missing membership. + name: "projects that can only be viewed and no permission to create says so", readOnly: []string{"payments"}, cannotCreate: true, repoDir: repoDirMyRepo, - wantErr: "you cannot create projects in this organization", + wantErr: "you can only view the projects in this organization, and cannot create a new one", + }, + { + // Seeing nothing at all is a different dead end: talking about the + // projects the caller can see, or about workflows, describes neither + // what happened nor what to ask an administrator for. + name: "an organization with no project in sight says the membership is missing", + cannotCreate: true, + repoDir: repoDirMyRepo, + wantErr: "you do not belong to any project in this organization, and cannot create a new one", }, }