Skip to content

✨ feat(prompt): add parent commit options to resolve 409 conflicts#723

Open
codekiln wants to merge 4 commits intomainfrom
m16-i719-prompt-push-fails-with-409-conflict-when-updating-
Open

✨ feat(prompt): add parent commit options to resolve 409 conflicts#723
codekiln wants to merge 4 commits intomainfrom
m16-i719-prompt-push-fails-with-409-conflict-when-updating-

Conversation

@codekiln
Copy link
Owner

@codekiln codekiln commented Jan 20, 2026

Summary

Resolves 409 conflicts when updating existing prompts by automatically fetching the latest commit as the parent, following Git-like UX expectations. No flags required - it just works.

Changes

SDK (sdk/src/prompts.rs, sdk/src/lib.rs)

  • Added CommitManifestResponse type with commit_hash and manifest fields
  • Added get_commit() method to fetch full commit metadata
  • Refactored pull() to use get_commit() internally
  • Exported CommitManifestResponse in public API

CLI (cli/src/commands/prompt.rs)

  • Removed --parent-commit, --auto-parent, and --force flags (over-engineered)
  • Made auto-parent the default behavior - automatic and transparent
  • New repos: no parent needed (first commit)
  • Existing repos: auto-fetch latest commit as parent
  • Graceful error handling with fallback to no parent

Tests

  • All 611 tests passing
  • No test changes needed (auto-parent is now default)

Behavior

For new prompts:

$ langstar prompt push -o owner -r new-prompt -t "template"
ℹ Checking if repository owner/new-prompt exists...
ℹ Repository not found, creating owner/new-prompt...
✓ Repository created successfully
ℹ New repository, no parent commit needed
ℹ Pushing prompt to owner/new-prompt...
✓ Pushed successfully

For existing prompts:

$ langstar prompt push -o owner -r existing-prompt -t "updated template"
ℹ Checking if repository owner/existing-prompt exists...
✓ Repository exists
ℹ Fetching latest commit as parent...
✓ Latest commit: abc123def456
ℹ Pushing prompt to owner/existing-prompt...
✓ Pushed successfully (commit: def789ghi012)

Error handling (graceful fallback):

$ langstar prompt push -o owner -r existing-prompt -t "template"
ℹ Checking if repository owner/existing-prompt exists...
✓ Repository exists
ℹ Fetching latest commit as parent...
⚠ Warning: Could not fetch latest commit: API error: 404
  Proceeding without parent commit (assuming first commit)
ℹ Pushing prompt to owner/existing-prompt...
✓ Pushed successfully

Design Decision: Minimal Fix

Issue #719 asked for: "The push should automatically fetch the latest commit hash and use it as the parent commit"

This PR delivers exactly that - no flags, no complexity, just automatic parent fetching like Git.

Why Not Flags?

PR #723 originally added three flags (--parent-commit, --auto-parent, --force), but this was over-engineered:

  • Users don't want to think about parent commits
  • The default behavior should "just work" like Git
  • Flags add cognitive overhead and decision fatigue
  • 99% of users want automatic behavior

Future Work (Out of Scope)

The docs/implementation/ls-prompt-ux-command-redesign.md design doc outlines a future CRUD redesign (Phase 2, Issue #668.2) where push will be split into create and update commands. This minimal fix:

Related Issues

Fixes #719

Test Plan

  • Added get_commit() SDK method with proper return type
  • Updated CLI to auto-fetch parent commit for existing repos
  • Graceful fallback if parent fetch fails
  • All 611 tests passing (cargo nextest run --profile ci --all-features --workspace)
  • Cargo fmt, check, and clippy passing
  • Manual testing: create new prompt, update existing prompt

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings January 20, 2026 13:02
@codekiln codekiln added this to the ls-prompt-ux milestone Jan 20, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

Unit Test Results

186 tests  ±0   186 ✅ ±0   1s ⏱️ ±0s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit e33cd3e. ± Comparison against base commit 1704f1e.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

SDK Integration Test Results

295 tests  +1   295 ✅ +1   32s ⏱️ +2s
 13 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit e33cd3e. ± Comparison against base commit 1704f1e.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

Integration Test Results

316 tests  ±0   316 ✅ ±0   4m 51s ⏱️ -17s
 13 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit e33cd3e. ± Comparison against base commit 1704f1e.

♻️ This comment has been updated with latest results.

Copy link
Contributor

Copilot AI left a comment

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 resolves 409 conflicts when updating existing prompts by adding three new CLI options to specify parent commits: --parent-commit, --auto-parent, and --force.

Changes:

  • Added CommitManifestResponse type to SDK with commit_hash and manifest fields
  • Added get_commit() method to fetch full commit metadata including hash
  • Refactored pull() to use get_commit() internally to reduce code duplication
  • Added three CLI flags with conflict constraints to handle parent commit scenarios
  • Updated test mocks to include commit_hash field in responses

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
sdk/src/prompts.rs Added get_commit() method and CommitManifestResponse type; refactored pull() to use new method
sdk/src/lib.rs Exported CommitManifestResponse in public API
cli/src/commands/prompt.rs Added three parent commit CLI flags with logic to auto-fetch, force push, or use explicit hash
sdk/tests/structured_prompts_test.rs Updated mock responses to include commit_hash field matching new API response structure

Copy link
Owner Author

Addressed Copilot Review Comments

Comment 1: Test Coverage for get_commit()

Fixed - Added dedicated test test_get_commit_success() in sdk/tests/structured_prompts_test.rs (line 273-330).

The test validates:

  • HTTP GET request to the correct endpoint
  • Proper authentication headers
  • Correct parsing of CommitManifestResponse structure
  • Verification of both commit_hash and manifest fields

Comment 2: Make --force Mutually Exclusive

Fixed - Updated --force flag to use conflicts_with_all = ["parent_commit", "auto_parent"] in cli/src/commands/prompt.rs (line 138).

This prevents ambiguous behavior when --force is combined with parent commit options.

Comment 3: Fix repo_exists Logic

Fixed - Changed repo_exists to false when repository creation fails in cli/src/commands/prompt.rs (line 665).

Updated error message to clarify that auto-parent will be disabled when creation fails.


All changes committed in cccfda5
All 610 tests passing

@codekiln codekiln force-pushed the m16-i719-prompt-push-fails-with-409-conflict-when-updating- branch from cccfda5 to 3c54b71 Compare January 23, 2026 12:24
Copilot AI review requested due to automatic review settings January 23, 2026 12:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

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

Comments suppressed due to low confidence (1)

cli/src/commands/prompt.rs:647

  • repo_exists detection treats any error from client.prompts().get(&repo_handle) as “not found” and proceeds to create_repo(). This can mis-handle non-404 failures (e.g., auth/scoping problems, transient 5xx), and for --auto-parent it can incorrectly disable parent resolution and then push without parent_commit (leading to another 409). Consider matching specifically on LangstarError::ApiError { status: 404, .. } for the create-repo path, and returning the original error for other statuses.
                let repo_exists = match client.prompts().get(&repo_handle).await {
                    Ok(_) => {
                        println!("✓ Repository exists");
                        true
                    }
                    Err(_) => {
                        formatter.info(&format!(
                            "Repository not found, creating {}...",
                            repo_handle
                        ));
                        match client

Comment on lines 333 to 334
async fn test_pull_structured_prompt_deserialization_error() {
let mut server = Server::new_async().await;
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In test_pull_structured_prompt_deserialization_error, the mocked GET /api/v1/commits response body is missing the required commit_hash field. Since pull() now deserializes into CommitManifestResponse (which requires commit_hash), the test will fail for the wrong reason (missing field) instead of exercising StructuredPrompt manifest deserialization. Update the mock response to include a valid commit_hash while keeping the manifest malformed.

Copilot uses AI. Check for mistakes.
Comment on lines 675 to 677
eprintln!("⚠ Warning: Using --force flag");
eprintln!(" This will push without parent commit validation");
eprintln!(" May overwrite concurrent changes to the prompt");
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--force currently just sets final_parent_commit to None, which is the same request behavior as the default (no --parent-commit/--auto-parent). Given the known 409 behavior when updating existing repos without parent_commit, this flag likely won’t do what the help text claims (“push without parent commit validation”). Either implement a real force mechanism supported by the API (if one exists), or rename/reword/remove the flag so it doesn’t promise behavior the request cannot provide.

Suggested change
eprintln!("⚠ Warning: Using --force flag");
eprintln!(" This will push without parent commit validation");
eprintln!(" May overwrite concurrent changes to the prompt");
eprintln!("⚠ Note: Using --force flag");
eprintln!(" Parent commit will not be automatically set for this push.");
eprintln!(" This behaves the same as omitting --parent-commit/--auto-parent; the server may still reject updates that require a parent commit.");

Copilot uses AI. Check for mistakes.
Comment on lines 129 to 139
/// Parent commit hash for updates (resolves 409 conflicts)
#[arg(long, value_name = "HASH", conflicts_with = "auto_parent")]
parent_commit: Option<String>,

/// Automatically fetch and use latest commit as parent
#[arg(long, conflicts_with = "parent_commit")]
auto_parent: bool,

/// Force push without parent commit validation (use with caution)
#[arg(long, conflicts_with_all = ["parent_commit", "auto_parent"])]
force: bool,
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New push behavior (--parent-commit, --auto-parent, --force) isn’t covered by existing CLI tests. There are already prompt CLI integration tests in cli/tests/prompt_structured_test.rs, but none exercise updating an existing repo with a second push using --auto-parent (or --parent-commit) to prevent 409s. Adding an integration test that (1) pushes once, (2) pushes again with --auto-parent (and/or explicit --parent-commit from the first response) would protect this new conflict-resolution path.

Copilot generated this review using guidance from repository custom instructions.
pub commit_hash: String,
/// The commit manifest (prompt template/schema)
pub manifest: serde_json::Value,
/// Optional example run IDs
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment for examples says “Optional example run IDs”, but the field is Option<Vec<serde_json::Value>> and the LangSmith schema (reference/api-specs/langsmith/prompt-schemas.json) defines examples as an array of RepoExampleResponse objects (not IDs). Update the comment to reflect that these are example run records (or consider introducing a typed RepoExampleResponse struct if you want stronger typing).

Suggested change
/// Optional example run IDs
/// Optional example run records (serialized RepoExampleResponse objects)

Copilot uses AI. Check for mistakes.
codekiln and others added 4 commits January 23, 2026 08:36
Adds three new CLI options to `langstar prompt push`:
- --parent-commit <HASH>: Specify parent commit explicitly
- --auto-parent: Automatically fetch latest commit as parent
- --force: Push without parent validation (with warnings)

This resolves 409 conflicts when updating existing prompts by providing
the parent commit reference that the LangSmith API requires.

SDK Changes:
- Add CommitManifestResponse type for full commit metadata
- Add get_commit() method to fetch commit hash and manifest
- Refactor pull() to use get_commit() internally

CLI Changes:
- Add new flags with conflicts_with constraint
- Implement auto-fetch logic for --auto-parent
- Add warnings for --force flag
- Pass parent_commit to SDK for both regular and structured prompts

Tests:
- Update mock responses to include commit_hash field
- All 609 tests passing

Fixes #719

Co-Authored-By: Claude <noreply@anthropic.com>
- Add dedicated test coverage for get_commit() method
- Make --force flag mutually exclusive with --parent-commit and --auto-parent
- Fix repo_exists logic when repository creation fails

All three changes improve code quality and prevent ambiguous behavior.
Tests: All 610 tests passing.

Co-Authored-By: Claude <noreply@anthropic.com>
Resolves 409 conflicts by automatically fetching latest commit as parent
when updating existing prompts, following Git-like UX expectations.

Changes:
- Remove --parent-commit, --auto-parent, and --force flags
- Make parent commit fetching automatic and transparent
- New repos: no parent needed (first commit)
- Existing repos: auto-fetch latest commit as parent
- Graceful error handling with fallback to no parent

Fixes #719

Co-Authored-By: Claude <noreply@anthropic.com>
@codekiln codekiln force-pushed the m16-i719-prompt-push-fails-with-409-conflict-when-updating- branch from 3c54b71 to e33cd3e Compare January 23, 2026 13:51
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.

prompt push fails with 409 conflict when updating existing prompt - missing parent commit option

1 participant