Collision guard: stacked PRs are not collisions (DL-025) - #53
Draft
jimutt wants to merge 5 commits into
Draft
Conversation
jimutt
force-pushed
the
fix/collision-guard-stacked-prs
branch
from
August 31, 2026 13:04
bd33912 to
08d1815
Compare
…lision DL-025. list-taken-ids.sh excluded only the PR whose head equals the current branch, so a branch stacked on the decisions PR — the normal DLD workflow — saw every decision it existed to implement reported as colliding with the base, with /dld-reindex prescribed as the fix even though nothing needed renaming. find-collisions.sh now requests the taken set with --exclude-contained, which also drops open PRs whose head is an ancestor of HEAD. The flag is opt-in and the filter sits at the call site: those IDs are still taken for allocation, since they exist in this branch's tree, and plan-renames.sh depends on the unfiltered answer when choosing free IDs for renamed records. An unfetched head ref stays in the set, so a missing fetch can't silently drop a real claim. Also adds scripts/install-opencode-plugin.sh, which symlinks the OpenCode plugins into a project instead of copying them and patching packageRoot() by regex. The regex was written for an older shape of that function, silently stopped matching, and left the copy resolving its scripts to the project root. The installer now verifies resolution before reporting success. 6 bats tests: default-keeps-stacked-IDs, --exclude-contained drops them, find-collisions ignores a stacked PR, unrelated-PR inclusion, unfetched-head fallback, own-branch exclusion.
The TUI surfaces never loaded. OpenCode discovers loose .ts files and
immediate plugin package directories under .opencode/plugins/, and it
loads a CLI plugin only when a package exposes it through a ./tui
export. The installer had been dropping tui.tsx into a plugins/tui/
subdirectory, which is not a discovery location, so only the server
half ever ran — and an earlier attempt to have one file serve both
roles produced 'has invalid tui export', since v1 plugin modules are
target-exclusive.
extensions/opencode-dld-run/ gains a package.json exporting '.' as the
server module and './tui' as the CLI module, and the installer links
the directory as one package instead of placing the two files
separately. Verification now also asserts both default exports match
the { id, setup } contract, so a wrong-shaped module fails the install
rather than silently loading half the plugin.
Co-Authored-By: Kimi K3 <noreply@pi.dev>
Generated-By: pi 0.84.2
A symlinked plugin *directory* is skipped by OpenCode's discovery — a symlink is not a directory to readdir — so the previous layout registered nothing at all, silently: no load line, no error. Symlinked loose files are picked up, which is why the earlier single-file layout worked and hid the rule. The installer now writes a real package directory whose three files are one-line re-exports of the modules in dld-kit, so the code still has a single home and edits take effect on reload. It also registers ./plugins/dld-run explicitly in .opencode/opencode.json, merging into an existing plugins array when jq is available, since an explicit entry is what the docs guarantee for loading a package's TUI component. Verified against a live standalone session: the server log now shows loading plugin id=.../.opencode/plugins/dld-run with no errors. Co-Authored-By: Kimi K3 <noreply@pi.dev> Generated-By: pi 0.84.2
The previous installer linked .opencode/plugins/dld-run straight at dld-kit's extension directory. When the new package-directory installer ran over that layout, mkdir -p was a no-op on the link and the subsequent rm -f and writes resolved through it, deleting dld-kit's own server.ts, tui.tsx and package.json and replacing them with shims that re-exported themselves — hence 'export default cannot be used with export *'. The link at that path is now removed before the real directory is created, and the installer refuses outright to write to any path resolving inside the dld-kit checkout. Regression-tested against a repo pre-seeded with the old symlink layout: the install converts it to a real package directory and dld-kit's sources are left untouched. Co-Authored-By: Kimi K3 <noreply@pi.dev> Generated-By: pi 0.84.2
package.json main/exports is not what the directory loader reads. With
the server entrypoint named server.ts, OpenCode logged 'configured
plugin directory has no index entrypoint' at WARN and dropped the
plugin from the location's plugin set — while still printing a
'loading plugin' INFO line for it, which made the failure look like a
success.
Renaming the shim to index.ts registers the plugin with both features:
dld-run | features={"server":true,"tui":true} | status=active
Verified against the running service via GET /api/plugin.
Co-Authored-By: Kimi K3 <noreply@pi.dev>
Generated-By: pi 0.84.2
Co-Authored-By: Kimi K3 <noreply@pi.dev>
Generated-By: pi 0.84.2
Co-Authored-By: Kimi K3 <noreply@pi.dev>
Generated-By: pi 0.84.2
jimutt
force-pushed
the
fix/collision-guard-stacked-prs
branch
from
August 31, 2026 16:11
08d1815 to
38f0271
Compare
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.
What
list-taken-ids.shnow skips open PRs whose head branch is an ancestor of HEAD, not just the PR whose head equals the current branch.Why
Stacking is the normal DLD workflow: decisions land as one PR, implementation branches are cut from it. Those branches contain the decisions PR's commits, so every decision they exist to implement was reported as a collision and the run refused to start — with
/dld-reindexprescribed, which would have had nothing to rename. Hit in a real project:origin/mainhad no decision records at all, yet DL-001..008 were all flagged.An unfetched head ref stays in the taken set, so a missing fetch can't silently disable the guard.
Also
scripts/install-opencode-plugin.sh— symlink-based OpenCode plugin install, replacing the throwaway copy-and-patch script that silently misresolvedpackageRoot()to the project root. Verifies resolution before reporting success.Tests
4 new bats tests (stacked exclusion, unrelated inclusion, unfetched fallback, own-branch exclusion), verified to fail without the fix. 309 bats, 140 bun, tsc, tessl green.