Skip to content

runtime: apply component lifetime scopes to embedded and managed modules #415

Description

@bomly-guy

Summary

Adopt the SDK component-lifetime contract from bomly-dev/bomly-sdk#26 across CLI registration and managed-plugin pooling so embedded and managed modules have equivalent teardown behavior.

Current behavior

Embedded and managed resources are owned by unrelated paths:

  • internal/registry/module.go:RegisterModule validates a module and constructs every embedded component with context.Background().
  • internal/registry/builder.go invokes that registration while building the registry, without a component cleanup handle.
  • internal/plugin/pool.go:ClientPool owns one managed subprocess per plugin for the command and restarts a dead process at most once.
  • ClientPool.Shutdown closes clients but cannot report teardown errors.
  • internal/plugin/runtime/hashicorp/runtime.go:Client.Close calls Kill; there is no graceful component cleanup handshake.

As a result, an embedded constructor cannot observe command cancellation or register teardown, while a managed component is cleaned up only as a side effect of process termination.

Dependency

Blocked on the released SDK contract in bomly-dev/bomly-sdk#26. Use a local go.work only during cross-repository development; do not commit a replace directive. The final CLI change must pin an SDK tag.

Required behavior

  • Propagate the command/component lifetime context into embedded module construction instead of creating a background context.
  • Retain the SDK cleanup scope for as long as the registry/component is usable, then close it exactly once.
  • Integrate managed subprocess shutdown with the same ownership model.
  • If the SDK adds an optional graceful shutdown RPC, call it with a bounded timeout and fall back to the existing hard process termination when an old plugin returns Unimplemented, the subprocess is already dead, or the grace period expires.
  • Run every registered cleanup even when earlier cleanup fails; retain actionable plugin/component identity in joined errors.
  • Preserve the existing one-restart bound and do not turn the command-scoped pool into a daemon.

Acceptance criteria

  • Embedded constructors receive a context cancelled with the owning command/component scope.
  • Embedded cleanups run LIFO and at most once on success, command cancellation, constructor failure after cleanup registration, and partial registry-build failure.
  • Managed cleanup is attempted before process termination when the negotiated SDK supports it.
  • Old plugins that do not implement graceful shutdown still terminate cleanly through the hard-kill fallback.
  • Subprocess death before shutdown does not panic, leak a pool entry, or cause duplicate cleanup.
  • Shutdown has a bounded duration and cannot hang CLI exit.
  • Cleanup failures are observable through the command error/logging path without skipping remaining cleanups.
  • Tests compare embedded and managed outcomes for success, cancellation, constructor failure, subprocess death, and repeated shutdown.
  • Existing pool concurrency and single-restart tests remain green.
  • The CLI pins a released SDK version; go.mod contains no replace.

Architecture and compatibility

Record or extend the SDK lifecycle ADR under dev-docs/adr/ before merging. This issue must not change the bomly.plugin.v1 contract independently; additive wire work belongs in the SDK issue. Unimplemented is a compatibility result, not a plugin failure.

Out of scope

  • Persistent agents, daemon process reuse, hot reload, or reactive reconfiguration.
  • Raising the restart limit.
  • Sandboxing native plugins.

Verification

Run:

make test
make build-lite

If the SDK pin or generated component data changes, also run make generate and commit the generated drift.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions