You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(harness-ops): plugins sync and converge cannot clear stale project-scope pins the CLI treats as current #6713
/harness-ops:plugins reports project-scope pins as behind, but the CLI it delegates to treats them as current, so neither sync nor converge can clear the divergence.
Observed 2026-10-10 in a worktree of this repository, with harness-ops 3.11.0 loaded:
fleet-state.sh shows project-scope records for this worktree at older versions than user scope and the catalog: ai-slop 0.13.4 (catalog 0.13.6), evals 0.5.3 (0.7.2), harness-ops 3.7.2 (3.11.0), harness-config 1.8.1 (1.13.5), instruction-placement 0.21.5 (0.22.1), plugin-quality 0.13.5 (0.14.7), skill-quality 0.28.1 (0.29.1). All seven records carry lastUpdated 2026-10-04.
claude plugin update ai-slop@melodic-software -s project, run from the worktree root, prints "ai-slop is already at the latest version (0.13.6)". The same happens for evals ("0.7.2"). The record in installed_plugins.json stays at 0.13.4 / 0.5.3.
An earlier sync run reported evals@melodic-software (project): 0.5.3 → 0.5.4 as updated. The record stayed at 0.5.3.
The session loads the user-scope build (this skill ran from cache/melodic-software/harness-ops/3.11.0), not the 3.7.2 project pin, so the stale records do not seem to be what runs.
Impact
sync reports an update that did not change the record, and the next run counts the same divergence again.
converge's update strategy is a no-op for these rows. Its only other strategy uninstalls the pin, which edits the committed .claude/settings.json: too heavy for a record that does not affect what loads.
Questions
Does claude plugin update -s project compare against the effective (user-scope) install instead of the project record?
Should fleet-state.sh mark a project record as inert when the session loads a different version, and leave it out of the actionable divergence count?
Repro
In a worktree whose installed_plugins.json project record for a plugin is older than user scope and the catalog, run /harness-ops:plugins sync.
Run claude plugin update <id> -s project from the worktree root.
Compare the record's version and lastUpdated before and after.
More data from a converge run on 2026-10-12 (harness-ops 3.13.2, Claude Code 2.1.296). All 166 lagging project pins whose directories exist were sent through the update strategy, (cd <projectPath> && claude plugin update <id> -s project). Every call exited 0, and the fleet state was re-read before each one.
projectPath kind
Pins
Record actually moved
Plain directory (not a git checkout)
43
43
Main checkout of this repo
7
6 (discipline printed "already at the latest version (0.18.0)" and stayed at 0.17.7)
git worktree checkouts of this repo (19)
116
0. Every call printed "already at the latest version", and the record kept its old version
No .claude/settings.json changed anywhere, so the record in context/scope-semantics.md that says update -s project does not write committed settings still holds.
This narrows the first question above: the no-op happens in linked worktrees, not for project scope in general. It looks like the CLI checks a record other than the worktree's own, probably the main checkout's. context/converge.md says worktrees "pin independently", but that was verified with uninstall on 2.1.228. With update, they do not behave independently.
What this means for converge as written:
For worktree rows, the update strategy is a guaranteed no-op that still reports success. The only strategy that moves the record is uninstall, which edits each worktree's tracked settings file.
Suggestion: have converge flag worktree rows (git rev-parse --git-dir differs from --git-common-dir) as "update has no effect here", and re-read each record after its update so a no-op shows up as a failure instead of "ok".
Decision recorded. The owner approved applying independently verified recommendations on 2026-10-12.
Should converge detect worktree rows (git-dir differs from git-common-dir), mark them 'update has no effect here', and re-read each record after an update so a no-op reports as a failure? → Yes, both
Basis: fix(harness-ops): plugins sync and converge cannot clear stale project-scope pins the CLI treats as current #6713 comment kyle-sexton 2026-10-12 (table: worktree 116 pins, 0 moved); plugins/harness-ops/skills/plugins/context/converge.md:87; context/scope-semantics.md:166
claude plugin update -s project reports success but leaves the record unchanged for pins in linked git worktrees (116 of 116 no-ops on 2026-10-12), so converge reports ok while nothing moves. The owner's comment proposes flagging worktree rows and re-reading each record after update; no PR exists.
Verified by an independent fresh-context check (confirmed, risk low): gh issue view 6713 incl. kyle-sexton 2026-10-12T13:40Z comment (116 worktree pins, 0 moved; suggestion to flag worktree rows and re-read); plugins/harness-ops/skills/plugins/context/converge.md:87-90 (worktrees pin independently, verified with uninstall on 2.1.228); plugins/harness-ops/skills/plugins/context/scope-semantics.md:164-166; plugins/harness-ops/skills/plugins/scripts/fleet-state.sh exists; git log origin/main -- plugins/harness-ops/skills/plugins (no fix since; #6932 only adds a stale-record pointer); gh pr search 6713: no PR
Admission to an agent lane stays with the work-class rules and owner ratification.
Owner batch-ratified this admission in an attended session on 2026-10-12: work-class: scoped. Rule: class C1-C3, no trust surface, the independent check confirmed the recorded decision, and no owner step remains.
Possibly the same root cause as #7445: on 2026-10-14, claude plugin update <id> -s project run from a repository's main checkout updated a sibling linked worktree's project-scope record instead of the current directory's. The CLI output named the worktree's path as the scope it updated. If the CLI matches project records by repository (the shared git common dir) or takes the first record for the id, rather than the literal cwd, it would also explain this issue's 'already at the latest version' from a worktree whose own record stays behind. #7445 has the evidence.
Type: Bug Summary: Make /harness-ops:plugins converge and fleet-state report stale project-scope pins in linked worktrees honestly instead of claiming an update fixed them.
Current behavior:
In a linked git worktree, claude plugin update -s project reports success but leaves the project-scope record unchanged (116 of 116 no-ops measured). sync reports the update as done, the next run counts the same divergence again, and converge's update strategy is a silent no-op for those rows.
Desired behavior:
Converge detects a linked worktree (the git dir differs from the git common dir) and marks its project-scope rows update has no effect here, instead of attempting or claiming an update.
After any update, converge and sync re-read the record; an update that left the version unchanged is reported as a failure, not a success.
fleet-state keeps a lagging project record in a linked worktree in the divergence count, labeled update ineffective; uninstall clears it.
Main checkouts behave as today.
Key interfaces:
fleet-state output: a label on lagging project records in linked worktrees.
Converge's update strategy: worktree detection and a post-update re-read.
Sync's result classification: a no-op update counts as failed.
Acceptance criteria:
A fleet-state test with a linked-worktree fixture shows the lagging record labeled update ineffective; uninstall clears it and still counted.
A test shows an update whose re-read version is unchanged classified as a failure.
The converge context states the worktree rule and the re-read.
scripts/run-test-file.sh plugins/harness-ops/skills/plugins/scripts/fleet-state.test.sh and scripts/run-test-file.sh plugins/harness-ops/skills/plugins/scripts/sync-run.test.sh pass.
A harness-ops changelog fragment is added and scripts/check-changelog-fragments.sh passes.
This was generated by AI during backlog shaping.Held, not admitted: this brief is drafted and checked, but the issue lane would stop on it today. It is admitted, by adding agent-ready, once the lane can run the skill-quality check scripts (Automation Lanes' PR #7912). Held under the owner's 2026-10-18 lane-narrowing course correction.
Another data point against this on 2026-10-20 (harness-ops 3.17.0, WSL): claude plugin update <id> -s project run from /mnt/c/Users/<user> moved all 52 lagging project records to the catalog version. A fleet-state.sh read-back after each call confirmed it, and the folder's .claude/settings.json hash did not change. Together with #7458's 8 of 8, this looks closable unless a repro still exists on a current CLI.
Still reproduces on a current CLI, from git worktrees (2026-10-20, Claude Code 2.1.304, harness-ops 3.17.0, WSL on melo-lap-001).
What ran
/harness-ops:plugins sync from each of 17 present checkouts of this repository (the main checkout plus 16 linked worktrees under ~/worktrees/). Together they held 144 project/local records behind the catalog, for example harness-ops 3.8.1–3.16.0 against 3.17.0.
What happened
Sync's in-repo step issued 164 claude plugin update <id> -s project|local calls across the 17 runs. All 164 printed ✔ <name> is already at the latest version (<catalog version>). None printed updated from.
Every run's post.melodic-software.json still showed 144 records behind. For example, harness-ops@melodic-software in feat-netlify-plugin stayed at 3.8.9 (installPath …/harness-ops/3.8.9) in both pre and post.
Sync reported this silently. Each digest had in_repo.updated: [] and in_repo.failed: [], and the render said In-repo: 0 project/local install(s) updated with nothing under Action needed. A record the CLI claimed was current but that fleet-state.sh still shows as behind should be surfaced as "did not move", the same way converge Step 4 reports it.
Workaround that worked:claude plugin uninstall <id> -s local --keep-data, then claude plugin install <id> -s local, run from the main checkout. That moved docs-naming from 0.2.7 to 0.3.3 and left git status clean.
Unattributed follow-on
Between 04:50 and 04:56 UTC, after the last sweep run ended at 04:48, 141 of these worktree records moved to the catalog version (by lastUpdated in installed_plugins.json) without any further CLI call from this session. My one local-scope reinstall was at 04:52:25. A fresh fleet-state.sh read at about 05:00 showed 0 present records behind. I did not identify the writer. It could have been a delayed write from the earlier update calls, background autoUpdate, or another session on this lane. I can't tell from what I have, so this issue may still be real even though the end state is clean.
The comment above, with 52 of 52 records moving from /mnt/c/Users/<user>, differs from this case in one way: these are linked worktrees of one repository that hold the same ids. That fits the #7445 sibling-record matching.
Activity
github-actions commentedon Oct 10, 2026
Thanks for the report. Automated triage handed this issue to a maintainer.
Automated triage. A maintainer reviews it.
kyle-sexton commentedon Oct 12, 2026
More data from a
convergerun on 2026-10-12 (harness-ops 3.13.2, Claude Code 2.1.296). All 166 lagging project pins whose directories exist were sent through the update strategy,(cd <projectPath> && claude plugin update <id> -s project). Every call exited 0, and the fleet state was re-read before each one.disciplineprinted "already at the latest version (0.18.0)" and stayed at 0.17.7)git worktreecheckouts of this repo (19)No
.claude/settings.jsonchanged anywhere, so the record incontext/scope-semantics.mdthat saysupdate -s projectdoes not write committed settings still holds.This narrows the first question above: the no-op happens in linked worktrees, not for project scope in general. It looks like the CLI checks a record other than the worktree's own, probably the main checkout's.
context/converge.mdsays worktrees "pin independently", but that was verified withuninstallon 2.1.228. Withupdate, they do not behave independently.What this means for converge as written:
git rev-parse --git-dirdiffers from--git-common-dir) as "update has no effect here", and re-read each record after its update so a no-op shows up as a failure instead of "ok".kyle-sexton commentedon Oct 13, 2026
Decision recorded. The owner approved applying independently verified recommendations on 2026-10-12.
Basis: fix(harness-ops): plugins sync and converge cannot clear stale project-scope pins the CLI treats as current #6713 comment kyle-sexton 2026-10-12 (table: worktree 116 pins, 0 moved); plugins/harness-ops/skills/plugins/context/converge.md:87; context/scope-semantics.md:166
Basis: judgment; fix(harness-ops): plugins sync and converge cannot clear stale project-scope pins the CLI treats as current #6713 body (session loads user-scope build, not the project pin)
claude plugin update -s projectreports success but leaves the record unchanged for pins in linked git worktrees (116 of 116 no-ops on 2026-10-12), so converge reports ok while nothing moves. The owner's comment proposes flagging worktree rows and re-reading each record after update; no PR exists.Verified by an independent fresh-context check (confirmed, risk low): gh issue view 6713 incl. kyle-sexton 2026-10-12T13:40Z comment (116 worktree pins, 0 moved; suggestion to flag worktree rows and re-read); plugins/harness-ops/skills/plugins/context/converge.md:87-90 (worktrees pin independently, verified with uninstall on 2.1.228); plugins/harness-ops/skills/plugins/context/scope-semantics.md:164-166; plugins/harness-ops/skills/plugins/scripts/fleet-state.sh exists; git log origin/main -- plugins/harness-ops/skills/plugins (no fix since; #6932 only adds a stale-record pointer); gh pr search 6713: no PR
Admission to an agent lane stays with the work-class rules and owner ratification.
kyle-sexton commentedon Oct 13, 2026
Owner batch-ratified this admission in an attended session on 2026-10-12: work-class: scoped. Rule: class C1-C3, no trust surface, the independent check confirmed the recorded decision, and no owner step remains.
kyle-sexton commentedon Oct 14, 2026
Possibly the same root cause as #7445: on 2026-10-14,
claude plugin update <id> -s projectrun from a repository's main checkout updated a sibling linked worktree's project-scope record instead of the current directory's. The CLI output named the worktree's path as the scope it updated. If the CLI matches project records by repository (the shared git common dir) or takes the first record for the id, rather than the literal cwd, it would also explain this issue's 'already at the latest version' from a worktree whose own record stays behind. #7445 has the evidence.kyle-sexton commentedon Oct 18, 2026
Agent Brief
Type: Bug
Summary: Make
/harness-ops:pluginsconverge and fleet-state report stale project-scope pins in linked worktrees honestly instead of claiming an update fixed them.Current behavior:
In a linked git worktree,
claude plugin update -s projectreports success but leaves the project-scope record unchanged (116 of 116 no-ops measured).syncreports the update as done, the next run counts the same divergence again, and converge's update strategy is a silent no-op for those rows.Desired behavior:
update has no effect here, instead of attempting or claiming an update.update ineffective; uninstall clears it.Key interfaces:
Acceptance criteria:
update ineffective; uninstall clears itand still counted.scripts/run-test-file.sh plugins/harness-ops/skills/plugins/scripts/fleet-state.test.shandscripts/run-test-file.sh plugins/harness-ops/skills/plugins/scripts/sync-run.test.shpass.scripts/check-changelog-fragments.shpasses.check-skillspasses on the PR.Out of scope:
.claude/settings.json.--jsonparsing in feat(harness-ops): read claude plugin --json output in plugins sync-run.sh instead of scraping text #6757.This was generated by AI during backlog shaping. Held, not admitted: this brief is drafted and checked, but the issue lane would stop on it today. It is admitted, by adding
agent-ready, once the lane can run the skill-quality check scripts (Automation Lanes' PR #7912). Held under the owner's 2026-10-18 lane-narrowing course correction.fix(harness-ops): require commondir for linkedWorktree and drop resta…
kyle-sexton commentedon Oct 20, 2026
Another data point against this on 2026-10-20 (harness-ops 3.17.0, WSL):
claude plugin update <id> -s projectrun from/mnt/c/Users/<user>moved all 52 lagging project records to the catalog version. Afleet-state.shread-back after each call confirmed it, and the folder's.claude/settings.jsonhash did not change. Together with #7458's 8 of 8, this looks closable unless a repro still exists on a current CLI.kyle-sexton commentedon Oct 20, 2026
Still reproduces on a current CLI, from git worktrees (2026-10-20, Claude Code 2.1.304, harness-ops 3.17.0, WSL on melo-lap-001).
What ran
/harness-ops:plugins syncfrom each of 17 present checkouts of this repository (the main checkout plus 16 linked worktrees under~/worktrees/). Together they held 144 project/local records behind the catalog, for example harness-ops 3.8.1–3.16.0 against 3.17.0.What happened
claude plugin update <id> -s project|localcalls across the 17 runs. All 164 printed✔ <name> is already at the latest version (<catalog version>). None printedupdated from.post.melodic-software.jsonstill showed 144 records behind. For example,harness-ops@melodic-softwareinfeat-netlify-pluginstayed at 3.8.9 (installPath …/harness-ops/3.8.9) in bothpreandpost.in_repo.updated: []andin_repo.failed: [], and the render saidIn-repo: 0 project/local install(s) updatedwith nothing underAction needed. A record the CLI claimed was current but thatfleet-state.shstill shows as behind should be surfaced as "did not move", the same way converge Step 4 reports it.claude plugin uninstall <id> -s local --keep-data, thenclaude plugin install <id> -s local, run from the main checkout. That moveddocs-namingfrom 0.2.7 to 0.3.3 and leftgit statusclean.Unattributed follow-on
Between 04:50 and 04:56 UTC, after the last sweep run ended at 04:48, 141 of these worktree records moved to the catalog version (by
lastUpdatedininstalled_plugins.json) without any further CLI call from this session. My one local-scope reinstall was at 04:52:25. A freshfleet-state.shread at about 05:00 showed 0 present records behind. I did not identify the writer. It could have been a delayed write from the earlier update calls, backgroundautoUpdate, or another session on this lane. I can't tell from what I have, so this issue may still be real even though the end state is clean.The comment above, with 52 of 52 records moving from
/mnt/c/Users/<user>, differs from this case in one way: these are linked worktrees of one repository that hold the same ids. That fits the #7445 sibling-record matching.