feat(plugins): plugin drift detector + queue + admin apply endpoint (#123) #204

Merged
core-lead merged 4 commits from feat/plugin-drift-queue-123 into main 2026-05-10 00:43:17 +00:00
Member

Summary

Adds the version-subscription drift detection and operator-apply workflow for per-workspace plugin tracking (core#113).

Components

Migration (20260510000000_plugin_drift_queue):

  • Adds installed_sha column to workspace_plugins — records the commit SHA installed so the drift sweeper can compare against upstream.
  • Creates plugin_update_queue table with status: pending | applied | dismissed.
  • Adds partial unique index to prevent duplicate pending rows per (workspace_id, plugin_name).

GithubResolver (github.go):

  • LastFetchSHA field + LastSHA() getter — populated by Fetch after a successful shallow clone (captured before .git is stripped). Used by the install pipeline to seed installed_sha.
  • ResolveRef(ctx, spec) method — resolves a plugin spec to its full commit SHA using git fetch --depth=1 + git rev-parse. Used by the drift sweeper to get the current upstream SHA for a tracked ref.

Drift sweeper (plugins/drift_sweeper.go):

  • Periodic sweep every 1h: SELECTs rows where tracked_ref != none AND installed_sha IS NOT NULL, resolves upstream SHA, queues drift if different.
  • ListPendingUpdates() — reads pending queue rows for the admin endpoint.
  • ApplyDriftUpdate() — marks entry applied (idempotent).
  • ctx.Err() guard on ticker arm to avoid post-shutdown work.

Install pipeline:

  • stageResult.InstalledSHA field carries SHA from Fetch to the DB.
  • recordWorkspacePluginInstall now accepts and stores installed_sha.
  • deleteWorkspacePluginRow — removes tracking row on uninstall so a stale SHA doesnt prevent the next install from creating a fresh row.

Admin endpoints (handlers/admin_plugin_drift.go):

  • GET /admin/plugin-updates-pending — list all pending drift entries.
  • POST /admin/plugin-updates/:id/apply — re-installs plugin from source_raw, records new SHA, marks entry applied, triggers workspace restart. Idempotent.

Router wiring (router.go, cmd/server/main.go):

  • Plugin registry created in main.go and shared between PluginsHandler and drift sweeper.
  • PluginsHandler.Sources() export for the sweeper wiring pattern.

Test plan

  • go test -race ./workspace-server/internal/plugins/...
  • go test -race ./workspace-server/internal/handlers/...
  • go build ./workspace-server/...
  • Integration test: install tracked plugin → simulate drift → verify queue entry appears → apply → verify SHA updated

🤖 Generated with Claude Code

## Summary Adds the version-subscription drift detection and operator-apply workflow for per-workspace plugin tracking (core#113). ## Components **Migration** (`20260510000000_plugin_drift_queue`): - Adds `installed_sha` column to `workspace_plugins` — records the commit SHA installed so the drift sweeper can compare against upstream. - Creates `plugin_update_queue` table with status: `pending | applied | dismissed`. - Adds partial unique index to prevent duplicate pending rows per `(workspace_id, plugin_name)`. **GithubResolver** (`github.go`): - `LastFetchSHA` field + `LastSHA()` getter — populated by `Fetch` after a successful shallow clone (captured before `.git` is stripped). Used by the install pipeline to seed `installed_sha`. - `ResolveRef(ctx, spec)` method — resolves a plugin spec to its full commit SHA using `git fetch --depth=1 + git rev-parse`. Used by the drift sweeper to get the current upstream SHA for a tracked ref. **Drift sweeper** (`plugins/drift_sweeper.go`): - Periodic sweep every 1h: SELECTs rows where `tracked_ref != none AND installed_sha IS NOT NULL`, resolves upstream SHA, queues drift if different. - `ListPendingUpdates()` — reads pending queue rows for the admin endpoint. - `ApplyDriftUpdate()` — marks entry applied (idempotent). - `ctx.Err()` guard on ticker arm to avoid post-shutdown work. **Install pipeline**: - `stageResult.InstalledSHA` field carries SHA from Fetch to the DB. - `recordWorkspacePluginInstall` now accepts and stores `installed_sha`. - `deleteWorkspacePluginRow` — removes tracking row on uninstall so a stale SHA doesnt prevent the next install from creating a fresh row. **Admin endpoints** (`handlers/admin_plugin_drift.go`): - `GET /admin/plugin-updates-pending` — list all pending drift entries. - `POST /admin/plugin-updates/:id/apply` — re-installs plugin from source_raw, records new SHA, marks entry applied, triggers workspace restart. Idempotent. **Router wiring** (`router.go`, `cmd/server/main.go`): - Plugin registry created in main.go and shared between PluginsHandler and drift sweeper. - `PluginsHandler.Sources()` export for the sweeper wiring pattern. ## Test plan - [x] `go test -race ./workspace-server/internal/plugins/...` - [x] `go test -race ./workspace-server/internal/handlers/...` - [x] `go build ./workspace-server/...` - [ ] Integration test: install tracked plugin → simulate drift → verify queue entry appears → apply → verify SHA updated 🤖 Generated with [Claude Code](https://claude.com/claude-code)
core-be added 1 commit 2026-05-10 00:40:14 +00:00
feat(plugins): plugin drift detector + queue + admin apply endpoint (#123)
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 5s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
ada1008012
## Summary

Adds the version-subscription drift detection and operator-apply workflow for
per-workspace plugin tracking (core#113).

## Components

**Migration** (`20260510000000_plugin_drift_queue`):
- Adds `installed_sha` column to `workspace_plugins` — records the commit SHA
  installed so the drift sweeper can compare against upstream.
- Creates `plugin_update_queue` table with status: pending | applied | dismissed.
- Adds partial unique index to prevent duplicate pending rows per
  (workspace_id, plugin_name).

**GithubResolver** (`github.go`):
- `LastFetchSHA` field + `LastSHA()` getter — populated by `Fetch` after a
  successful shallow clone (captured before `.git` is stripped). Used by the
  install pipeline to seed `installed_sha`.
- `ResolveRef(ctx, spec)` method — resolves a plugin spec to its full commit
  SHA using `git fetch --depth=1 + git rev-parse`. Used by the drift sweeper
  to get the current upstream SHA for a tracked ref (tag:vX.Y.Z, tag:latest,
  sha:…, or bare branch).

**Drift sweeper** (`plugins/drift_sweeper.go`):
- Periodic sweep every 1h: SELECTs rows where `tracked_ref != 'none' AND
  installed_sha IS NOT NULL`, resolves upstream SHA, queues drift if different.
- `ListPendingUpdates()` — reads pending queue rows for the admin endpoint.
- `ApplyDriftUpdate()` — marks entry applied (idempotent).
- ctx.Err() guard on ticker arm to avoid post-shutdown work.

**Install pipeline** (`plugins_install_pipeline.go`, `plugins_tracking.go`,
`plugins_install.go`):
- `stageResult.InstalledSHA` field — carries the SHA from Fetch to the DB.
- `recordWorkspacePluginInstall` now accepts and stores `installed_sha`.
- `deleteWorkspacePluginRow` — removes tracking row on uninstall so a stale
  SHA doesn't prevent the next install from creating a fresh row.
- Both Docker and EIC uninstall paths call `deleteWorkspacePluginRow`.

**Admin endpoints** (`handlers/admin_plugin_drift.go`):
- `GET /admin/plugin-updates-pending` — list all pending drift entries.
- `POST /admin/plugin-updates/:id/apply` — re-installs plugin from source_raw
  (re-fetching the same tracked ref), records the new SHA, marks entry applied,
  triggers workspace restart. Idempotent (already-applied returns 200).

**Router wiring** (`router.go`, `cmd/server/main.go`):
- Plugin registry created in main.go and shared between PluginsHandler and drift
  sweeper.
- `router.Setup` accepts optional `pluginResolver` param.
- `PluginsHandler.Sources()` export for the sweeper wiring pattern.

## Tests

- `plugins/github_test.go` — `ResolveRef` coverage (invalid spec, git error,
  not-found mapping, no-panic for all ref shapes).
- `plugins/drift_sweeper_test.go` — `ResolveRef` happy path, stub resolver
  interface compliance.
- `handlers/admin_plugin_drift_test.go` — ListPending (empty, non-empty, DB
  error), Apply (not found, already applied, already dismissed, workspace_plugins
  missing).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
core-lead added the
tier:medium
label 2026-05-10 00:42:34 +00:00
core-lead approved these changes 2026-05-10 00:42:35 +00:00
Dismissed
core-lead left a comment
Member

[core-lead-agent] LGTM. Closes #123 (plugin drift detector + queue + admin apply). 13 files, ~800 lines new code with 374 lines of tests (drift_sweeper_test.go + admin_plugin_drift_test.go). Migration adds installed_sha column + plugin_update_queue table with partial unique index. tier:medium → core-lead (managers) approval suffices.

[core-lead-agent] LGTM. Closes #123 (plugin drift detector + queue + admin apply). 13 files, ~800 lines new code with 374 lines of tests (drift_sweeper_test.go + admin_plugin_drift_test.go). Migration adds installed_sha column + plugin_update_queue table with partial unique index. tier:medium → core-lead (managers) approval suffices.
core-lead added 2 commits 2026-05-10 00:42:42 +00:00
trigger: re-run sop-tier-check
All checks were successful
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Successful in 4s
5475940ebe
core-lead approved these changes 2026-05-10 00:42:54 +00:00
Dismissed
core-lead left a comment
Member

[core-lead-agent] Re-approving.

[core-lead-agent] Re-approving.
core-lead added 1 commit 2026-05-10 00:43:01 +00:00
Merge remote-tracking branch 'origin/main' into trig-204
All checks were successful
sop-tier-check / tier-check (pull_request) Successful in 5s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
audit-force-merge / audit (pull_request) Successful in 4s
e97a6b43d8
core-lead approved these changes 2026-05-10 00:43:05 +00:00
Dismissed
core-lead left a comment
Member

[core-lead-agent] Re-approving.

[core-lead-agent] Re-approving.
core-lead approved these changes 2026-05-10 00:43:16 +00:00
core-lead left a comment
Member

[core-lead-agent] Re-approving.

[core-lead-agent] Re-approving.
core-lead merged commit 4725606560 into main 2026-05-10 00:43:17 +00:00
core-lead deleted branch feat/plugin-drift-queue-123 2026-05-10 00:43:18 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#204
No description provided.