Skip to content

fix(just): bind empty assoc array; add plugins-reconcile-keys - #343

Merged
laurigates merged 1 commit into
mainfrom
chore/plugins-reconcile-keys
Jul 28, 2026
Merged

fix(just): bind empty assoc array; add plugins-reconcile-keys#343
laurigates merged 1 commit into
mainfrom
chore/plugins-reconcile-keys

Conversation

@laurigates

Copy link
Copy Markdown
Owner

Two changes to the canonical Claude-plugins recipes, both needed by the new weekly plugins-audit routine in ~/repos/.routines/.

1. _plugins-annotate crashed on zero-plugin repos

/Users/lgates/tmp/just-bmqgEJ/_plugins-annotate: line 179: seen: unbound variable

Under set -u, bash 5.3 treats a declare -A seen that is never assigned as unbound, so ${#seen[@]} aborts the recipe. It fired on exactly the repos an audit most needs to report — OLMap and kicad-mcp, which have a claude.yml workflow but zero plugins enabled. declare -A seen=() binds it.

Reproduce:

bash -c 'set -uo pipefail; declare -A seen; echo "${#seen[@]}"'   # seen: unbound variable
bash -c 'set -uo pipefail; declare -A seen=(); echo "${#seen[@]}"' # 0

2. New plugins-reconcile-keys — the unattended-safe half of plugins-sync-repo

Reconciles the key set of an exhaustive pin against canonical while leaving each repo's own enable/disable values alone:

  • + adds canonical plugins the repo omits
  • drops pins for plugins no longer in the marketplace
  • = never touches the value of a key present in both

The distinction is load-bearing, and is why the existing plugins-sync-repo must stay a deliberate interactive operation:

Is it a bug?
Missing key Yes. enabledPlugins is a whole-map replacement, so a plugin omitted from an exhaustive pin is silently disabled in web/remote sessions.
Differing value No. Stack-specific, and drifts in both directions on purpose — infrastructure keeps terraform-plugin and kubernetes-plugin on where canonical has them off.

Measured across the four exhaustive-pinned repos before writing this:

repo value drift canonical=false, repo=true canonical=true, repo=false
infrastructure 6 4 2
thelma 13 5 8
dev-workflow-overview 16 8 8
claude-plugins 17 17 0

Blanket-syncing those would disable exactly the plugins each repo needs.

Also refuses to run when the marketplace fetch fails, rather than treating an empty response as "every plugin was removed" — which would strip pins portfolio-wide.

Verification

$ just -g _plugins-annotate ~/repos/ForumViriumHelsinki/OLMap
/Users/lgates/repos/ForumViriumHelsinki/OLMap	enabled=0 lauri=0 none wf=claude.yml,claude-review.yml tracked   # was: unbound variable

$ just -g plugins-reconcile-keys ~/repos/ForumViriumHelsinki/infrastructure
  + comfyui-plugin / foundryvtt-plugin / session-plugin / software-design-plugin
  - upstream-pr-plugin        # gone from the marketplace

$ just -g plugins-reconcile-keys ~/repos/laurigates/loractl
Deliberate subset (8 < 30) — untouched

Re-running is a no-op ("Key set already canonical"). The infrastructure test write was reverted; the real change will land via the routine's PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_012zrh72rhT9VtdDYKnjRyVh

Two changes to the canonical plugins recipes.

1. `_plugins-annotate` aborted with "seen: unbound variable" for any repo
   with zero laurigates plugins. Under `set -u`, bash 5.3 treats a
   `declare -A seen` that is never assigned as UNBOUND, so `${#seen[@]}`
   fails — and it failed on exactly the repos an audit most needs to
   report (OLMap, kicad-mcp: a claude.yml present with nothing enabled).
   `declare -A seen=()` binds it.

2. New `plugins-reconcile-keys` — the safe half of `plugins-sync-repo`,
   for unattended use. It reconciles the key SET of an exhaustive pin
   against canonical (adds omitted canonical plugins, drops pins for
   plugins gone from the marketplace) while leaving each repo's own
   enable/disable VALUES alone.

   The distinction is load-bearing. A missing key is a real functional
   bug: enabledPlugins is a whole-map replacement, so a plugin omitted
   from an exhaustive pin is silently disabled in web/remote sessions.
   A differing value is not a bug — it is stack-specific and drifts in
   both directions on purpose (`infrastructure` keeps terraform-plugin
   and kubernetes-plugin ON where canonical has them OFF). Blanket
   `plugins-sync-repo` would disable exactly the plugins a repo needs,
   so it stays a deliberate, interactive operation.

   Refuses to run if the marketplace fetch fails, rather than treating
   an empty response as "every plugin was removed".

Consumed by the new weekly ~/repos/.routines/plugins-audit.sh sweep.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zrh72rhT9VtdDYKnjRyVh
@laurigates laurigates self-assigned this Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Broken Links Detected

Summary

Status Count
🔍 Total 170
✅ Successful 160
⏳ Timeouts 0
🔀 Redirected 0
👻 Excluded 9
❓ Unknown 0
🚫 Errors 1

Errors per input

Errors in exact_dot_claude/docs/prds/daily-catchup.PRD.md


Please fix the broken links before merging.

@laurigates
laurigates merged commit b812bd1 into main Jul 28, 2026
7 of 8 checks passed
@laurigates
laurigates deleted the chore/plugins-reconcile-keys branch July 28, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant