Repository navigation
Conversation
A sync can now plan tools and skills as rows of their own. A scope selects the kinds to plan: prompts, tools, skills, or all. A tool or skill row uses the same three-way rules, fingerprints, and sync.lock rows as a variation, and sorts before the variations that pin it. No command selects a scope yet, so sync prompts plans the same rows as before, with one fix: a variation row that would push or pull a tool or skill past an unsynced change on the other side is now a conflict.
cursor
Bot
force-pushed
the
ctawiah/plan-attachment-rows-1e8a
branch
from
October 22, 2026 05:24
5a7070e to
a899488
Compare
ctawiah
added this pull request to stack #942
October 22, 2026 13:28
This was referenced Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
ldcli sync promptskeeps local prompt files in step with the variations of LaunchDarkly AI Configs. A variation can use tools and skills, and it pins one version of each. Locally, a tool is a JSON file and a skill is a Markdown file. The code calls both attachments.Today the planner plans only variations. It compares a tool or a skill with LaunchDarkly only as content inside the variations that use it. This causes two problems:
This PR plans each tool and skill as a row of its own, limited by a scope. Later PRs apply those rows and add the
sync all,sync tools, andsync skillscommands.What changes
Scope(prompts,tools,skills, orall) sets which kinds a sync plans. No command selects a scope yet, sosync promptsplans the same rows as before, except for the guard below.planAttachmentsbuilds one row for each tool or skill that has a local file or async.lockrow.sync.lockstores the state that a working copy last agreed on with LaunchDarkly. The rules are the same as for a variation.checkAttachmentBaselines, makes a variation row aconflictwhen the row will overwrite a tool edit. This fixes the shared-tool case.delete_localrow of the tool becomes anerror. The exception is a plan that also pulls that variation without the reference.create_serverrow creates a resource in LaunchDarkly. If LaunchDarkly will reject its key, name, or description, the row becomes anerrorrow, so the apply cannot fail halfway.upsertgets a message that names both fixes. Target flag errors name the command of the scope, such asldcli sync tools add.The shared-tool case, as it works on
main:With this PR, the second row becomes a conflict, and one conflict choice covers both variations.
The message for a missing tool:
Design notes
planAttachmentsreusesFingerprintAttachmentandchooseAction.chooseActionandresolvedConflictActionnow usehasLocal()andhasServer(). Without that change, alocalconflict choice for a tool always becomes a create.Scopesits onSyncActionnext to theFilterfrom feat: add target flags to sync prompts and read sync flags only from the command line #938, so the sync afterattachorlinkstays a prompts sync.local.CompileAttachmentsparses only the files that the scope and the filter keep, so an invalid tool file does not breaksync skills. It uses the symbolic-link rules ofCompileWorkspace.allscope reads a shared tool once.sync.lockrow of the tool. A variation push that creates a new tool withupsertdoes not change, because LaunchDarkly has no copy on that variation.syncdomain.ValidateCreatecopies the create rules of the API. LaunchDarkly stays the authority.usedByfield toPlannedResource, because only this rule needs it.executePlanapply tool and skill rows.Review focus
sync promptsplans.hydratestill fail on a missing attachment whilereadreturns nil?Verification
go test ./...golangci-lint run(v2.9.0)fingerprint_golden_test.goand every existing test pass without edits. The one exception is a plain-text reason test that needed a resource ID.TestPromptCreatesMissingToolWhenUpsertIsEnabledstill passes.TestCheckAttachmentBaselinesStopsAVariationFromOverwritingAnUnsyncedEditcovers the guard with a tracked edit, an untracked conflict, and a push that adds a tool.TestPromptPlansToolAndSkillRowsByScoperuns a dry run in each scope against an API fake that returns 404 for absent tools and skills.TestPromptFiltersToolRowsBeforeAnyReadmakes sure that a tool filter reads only its tool.mainwith a built binary. With this PR, the plan shows the conflict.--conflict localthen publishes the tool once, as version 2, and both variations pin version 2.Related changes
--diffflag