Repository navigation
feat: plan standalone tool and skill rows by sync scope - #939
Merged
Merged
Conversation
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
cursor
Bot
force-pushed
the
ctawiah/plan-attachment-rows-1e8a
branch
from
October 22, 2026 15:07
a899488 to
ca26912
Compare
cursor
Bot
force-pushed
the
ctawiah/plan-attachment-rows-1e8a
branch
from
October 22, 2026 15:15
ca26912 to
e5d32e6
Compare
ctawiah
marked this pull request as ready for review
October 22, 2026 15:43
ctawiah
requested review from
btesfamariam and
nkester-ld
and removed request for
a team
October 22, 2026 15:43
cursor
Bot
force-pushed
the
ctawiah/plan-attachment-rows-1e8a
branch
from
October 22, 2026 15:47
e5d32e6 to
fb8b0d7
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8d8114a. Configure here.
nkester-ld
reviewed
Oct 22, 2026
nkester-ld
reviewed
Oct 22, 2026
nkester-ld
approved these changes
Oct 22, 2026
ctawiah
force-pushed
the
ctawiah/plan-attachment-rows-1e8a
branch
from
October 22, 2026 19:29
0fd0962 to
87de606
Compare
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.
The error for a missing skill told the user to add upsert: true to the front matter. A skill file has no upsert key yet, so that edit made the file fail to parse. The error now gives only the detach step for a skill. A tool still gets the upsert step.
A delete of a tool or a skill that a local variation still uses becomes an error. Before, only a pull of a version without the reference kept the delete. A delete of the variation in the same plan, or a conflict that LaunchDarkly wins, also made the delete an error, and that error stopped the whole plan. A variation delete now keeps the tool or skill delete. A conflict keeps it if the LaunchDarkly version has no reference. After the conflict choices, sync checks the deletes again. If the local version of the variation wins, the delete becomes an error.
A conflict choice applies to every row in the group of the conflict. A tool or skill delete joined the group of a variation conflict that uses it. A local choice then made the delete a create, and sync made the tool again in LaunchDarkly. A deleted tool or skill now stays out of conflict groups. After the choice, sync checks the delete again. A local choice makes the delete an error, and a LaunchDarkly choice keeps the delete.
ctawiah
force-pushed
the
ctawiah/plan-attachment-rows-1e8a
branch
from
October 22, 2026 19:33
87de606 to
125ca05
Compare
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
--diffflagNote
Overview
Adds scoped sync planning so tools and skills get their own plan rows (not only as embedded variation content), using the same three-way
sync.lockrules as prompts.Scope(prompts,tools,skills,all) onSyncActionlimits which kinds are planned; filters validate against the scope’s key kind.CompileAttachmentsreads managed tool/skill files (with symlink rejection and scope-aware inclusion).planAttachmentsbuilds rows for any local file or lock entry; plans sort skill → tool → variation so apply order can publish attachments before variations pin them.Safety / UX:
checkAttachmentBaselinesupgrades variation rows to conflict when they would overwrite a tool/skill edit relative to the lock (fixes shared-tool overwrite).checkAttachmentDeletesblocks deleting local tool/skill files while a variation still references them, with re-check after conflict resolution.ValidateCreatepre-validates create keys/names/descriptions. Conflict groups now tie tool/skill rows to variations that share changed attachments; deleted-on-LD tool rows stay out of groups so “use local” cannot recreate them. Filter typo errors point at scope-specificldcli sync … addcommands.CLI wiring for non-prompt scopes is deferred; default scope keeps
sync promptsbehavior except for the baseline guard.Reviewed by Cursor Bugbot for commit 0fd0962. Bugbot is set up for automated code reviews on this repo. Configure here.