Skip to content

Commit 024a22b

Browse files
Alex HolmbergAlex Holmberg
authored andcommitted
bug: hallucinated project_id
The agent created a hallucinated project_id when deploying for the syncable platform. Instead of relying context, the project_id is now ALWAYS derrived directly from the session, where users attach them selves to a project.
1 parent aab4bf3 commit 024a22b

5 files changed

Lines changed: 53 additions & 44 deletions

File tree

src/agent/tools/platform/create_deployment_config.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@ use crate::platform::api::types::{
1313
build_cloud_runner_config_v2,
1414
};
1515
use crate::platform::api::{PlatformApiClient, PlatformApiError};
16+
use crate::platform::session::PlatformSession;
1617
use std::str::FromStr;
1718

1819
/// Arguments for the create deployment config tool
1920
#[derive(Debug, Deserialize)]
2021
pub struct CreateDeploymentConfigArgs {
21-
/// The project UUID
22-
pub project_id: String,
2322
/// Service name for the deployment
2423
pub service_name: String,
2524
/// Repository ID from GitHub integration
@@ -102,7 +101,6 @@ A deployment config defines how to build and deploy a service, including:
102101
- Auto-deploy settings
103102
104103
**Required Parameters:**
105-
- project_id: The project UUID
106104
- service_name: Name for the service (lowercase, hyphens allowed)
107105
- repository_id: GitHub repository ID (from platform GitHub integration)
108106
- repository_full_name: Full repo name like "owner/repo"
@@ -138,10 +136,6 @@ A deployment config defines how to build and deploy a service, including:
138136
parameters: json!({
139137
"type": "object",
140138
"properties": {
141-
"project_id": {
142-
"type": "string",
143-
"description": "The UUID of the project"
144-
},
145139
"service_name": {
146140
"type": "string",
147141
"description": "Name for the service (lowercase, hyphens allowed)"
@@ -218,27 +212,38 @@ A deployment config defines how to build and deploy a service, including:
218212
}
219213
},
220214
"required": [
221-
"project_id", "service_name", "repository_id", "repository_full_name",
215+
"service_name", "repository_id", "repository_full_name",
222216
"port", "branch", "target_type", "provider", "environment_id"
223217
]
224218
}),
225219
}
226220
}
227221

228222
async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
229-
// Validate required fields
230-
if args.project_id.trim().is_empty() {
223+
// Load project_id from session (authoritative source — prevents stale IDs from LLM context)
224+
let session = match PlatformSession::load() {
225+
Ok(s) => s,
226+
Err(_) => {
227+
return Ok(format_error_for_llm(
228+
"create_deployment_config",
229+
ErrorCategory::InternalError,
230+
"Failed to load platform session",
231+
Some(vec!["Try authenticating with `sync-ctl auth login`"]),
232+
));
233+
}
234+
};
235+
236+
if !session.is_project_selected() {
231237
return Ok(format_error_for_llm(
232238
"create_deployment_config",
233239
ErrorCategory::ValidationFailed,
234-
"project_id cannot be empty",
235-
Some(vec![
236-
"Use list_projects to find valid project IDs",
237-
"Use current_context to get the selected project",
238-
]),
240+
"No project selected",
241+
Some(vec!["Use select_project to choose a project first"]),
239242
));
240243
}
241244

245+
let project_id = session.project_id.clone().unwrap_or_default();
246+
242247
if args.service_name.trim().is_empty() {
243248
return Ok(format_error_for_llm(
244249
"create_deployment_config",
@@ -316,7 +321,7 @@ A deployment config defines how to build and deploy a service, including:
316321
if let Some(ref provider) = provider_enum {
317322
if matches!(provider, CloudProvider::Gcp | CloudProvider::Azure) {
318323
if let Ok(credential) = client
319-
.check_provider_connection(provider, &args.project_id)
324+
.check_provider_connection(provider, &project_id)
320325
.await
321326
{
322327
if let Some(cred) = credential {
@@ -351,7 +356,7 @@ A deployment config defines how to build and deploy a service, including:
351356
// Note: Send both field name variants (dockerfile/dockerfilePath, context/buildContext)
352357
// for backend compatibility - different endpoints may expect different field names
353358
let request = CreateDeploymentConfigRequest {
354-
project_id: args.project_id.clone(),
359+
project_id,
355360
service_name: args.service_name.clone(),
356361
repository_id: args.repository_id,
357362
repository_full_name: args.repository_full_name.clone(),

src/agent/tools/platform/trigger_deployment.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ use serde_json::json;
99

1010
use crate::agent::tools::error::{ErrorCategory, format_error_for_llm};
1111
use crate::platform::api::{PlatformApiClient, PlatformApiError, TriggerDeploymentRequest};
12+
use crate::platform::session::PlatformSession;
1213

1314
/// Arguments for the trigger deployment tool
1415
#[derive(Debug, Deserialize)]
1516
pub struct TriggerDeploymentArgs {
16-
/// The project ID for the deployment
17-
pub project_id: String,
1817
/// The deployment config ID to use
1918
pub config_id: String,
2019
/// Optional specific commit SHA to deploy
@@ -56,13 +55,12 @@ Starts a new deployment for the specified config. Returns a task ID that can be
5655
used to monitor deployment progress with `get_deployment_status`.
5756
5857
**Parameters:**
59-
- project_id: The project UUID
60-
- config_id: The deployment config ID (get from list_deployment_configs)
58+
- config_id: The deployment config ID (get from list_deployment_configs or create_deployment_config)
6159
- commit_sha: Optional specific commit to deploy (defaults to latest on branch)
6260
6361
**Prerequisites:**
6462
- User must be authenticated via `sync-ctl auth login`
65-
- A deployment config must exist for the project
63+
- A deployment config must exist (use create_deployment_config first if needed)
6664
6765
**Use Cases:**
6866
- Deploy the latest code from a branch
@@ -77,50 +75,57 @@ used to monitor deployment progress with `get_deployment_status`.
7775
parameters: json!({
7876
"type": "object",
7977
"properties": {
80-
"project_id": {
81-
"type": "string",
82-
"description": "The UUID of the project"
83-
},
8478
"config_id": {
8579
"type": "string",
86-
"description": "The deployment config ID (from list_deployment_configs)"
80+
"description": "The deployment config ID (from list_deployment_configs or create_deployment_config)"
8781
},
8882
"commit_sha": {
8983
"type": "string",
9084
"description": "Optional: specific commit SHA to deploy (defaults to latest)"
9185
}
9286
},
93-
"required": ["project_id", "config_id"]
87+
"required": ["config_id"]
9488
}),
9589
}
9690
}
9791

9892
async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
99-
// Validate project_id
100-
if args.project_id.trim().is_empty() {
93+
// Validate config_id
94+
if args.config_id.trim().is_empty() {
10195
return Ok(format_error_for_llm(
10296
"trigger_deployment",
10397
ErrorCategory::ValidationFailed,
104-
"project_id cannot be empty",
98+
"config_id cannot be empty",
10599
Some(vec![
106-
"Use list_projects to find valid project IDs",
107-
"Use select_project to set the current project context",
100+
"Use list_deployment_configs to find available deployment configs",
108101
]),
109102
));
110103
}
111104

112-
// Validate config_id
113-
if args.config_id.trim().is_empty() {
105+
// Load project_id from session (authoritative source)
106+
let session = match PlatformSession::load() {
107+
Ok(s) => s,
108+
Err(_) => {
109+
return Ok(format_error_for_llm(
110+
"trigger_deployment",
111+
ErrorCategory::InternalError,
112+
"Failed to load platform session",
113+
Some(vec!["Try authenticating with `sync-ctl auth login`"]),
114+
));
115+
}
116+
};
117+
118+
if !session.is_project_selected() {
114119
return Ok(format_error_for_llm(
115120
"trigger_deployment",
116121
ErrorCategory::ValidationFailed,
117-
"config_id cannot be empty",
118-
Some(vec![
119-
"Use list_deployment_configs to find available deployment configs",
120-
]),
122+
"No project selected",
123+
Some(vec!["Use select_project to choose a project first"]),
121124
));
122125
}
123126

127+
let project_id = session.project_id.clone().unwrap_or_default();
128+
124129
// Create the API client
125130
let client = match PlatformApiClient::new() {
126131
Ok(c) => c,
@@ -131,7 +136,7 @@ used to monitor deployment progress with `get_deployment_status`.
131136

132137
// Build the request
133138
let request = TriggerDeploymentRequest {
134-
project_id: args.project_id.clone(),
139+
project_id,
135140
config_id: args.config_id.clone(),
136141
commit_sha: args.commit_sha.clone(),
137142
};

src/platform/api/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ impl PlatformApiClient {
722722
request: &TriggerDeploymentRequest,
723723
) -> Result<TriggerDeploymentResponse> {
724724
log::debug!(
725-
"Triggering deployment: POST /api/deployment-configs/deploy with projectId={}, configId={}",
725+
"Triggering deployment: POST /api/deployment-configs/deploy with projectId={} configId={}",
726726
request.project_id,
727727
request.config_id
728728
);

src/platform/api/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ pub struct CreateDeploymentConfigResponse {
335335
#[derive(Debug, Clone, Serialize)]
336336
#[serde(rename_all = "camelCase")]
337337
pub struct TriggerDeploymentRequest {
338-
/// Project ID for the deployment
338+
/// The project UUID this config belongs to
339339
pub project_id: String,
340340
/// Deployment config ID to use
341341
pub config_id: String,

src/wizard/orchestrator.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,7 @@ pub async fn run_wizard(
525525

526526
// Debug: Show trigger request
527527
log::debug!(
528-
"Trigger request: projectId={}, configId={}",
529-
trigger_request.project_id,
528+
"Trigger request: configId={}",
530529
trigger_request.config_id
531530
);
532531

0 commit comments

Comments
 (0)