Skip to content

feat(plugins): add native-worker capabilities for remote executor plugins - #4934

Open
nova28 wants to merge 90 commits into
kdlbs:mainfrom
nova28:feature/core-spec-provider-c-9ve
Open

nova28 wants to merge 90 commits into
kdlbs:mainfrom
nova28:feature/core-spec-provider-c-9ve

Conversation

@nova28

@nova28 nova28 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Tip

PR walkthrough: Open the visual walkthrough

Remote executor plugins cannot own an environment safely today: the host gives them no workspace path, no repository intent, no proof the agent stopped before destroy and no durable record of a half-finished cleanup, so a crash or an archive mid-launch can orphan the remote resource. This adds those host capabilities behind negotiated, versioned capabilities, so a plugin that opts in can provision, connect, drain, destroy and recover without leaking, and plugins that opt into nothing behave as before.

Important Changes

  • Capability negotiation: a versioned host capability registry, echoed on profile validation; allocation is refused when host and provider disagree.
  • Workspace and repository: the workspace descriptor (path and identity) and repository intent are passed to provision.
  • Tunnel: a host-opened tunnel for agentctl control and API traffic, bound to installation, environment, generation and port.
  • Drain and cleanup: destroy requires a drain receipt. The task-environment cleanup job is the single authority that drains and destroys on archive. Claim-scoped cleanup_progress and recovery_progress checkpoints fence stale writers.
  • Drain contract table: the cleanup reason and drain outcome pairs a provider may rely on are listed in the system design. not_attempted is sent only with launch_failed and only when no agentctl handshake ever started; each cleanup claim acquisition has a unique id that the provider echoes on checkpoints; a bounded periodic sweep retries handoffs and retained cleanup_pending rows.
  • Durable bootstrap: durable bootstrap and guarded creating-to-ready publication across host restarts.
  • No orphaned resources: a resource a launch provisioned but could not record is stored in unrecorded_plugin_executors and destroyed by the cleanup under its own claim, so archiving during provisioning does not orphan it. That destroy carries the launch cleanup reason launch_failed and drain_outcome: not_attempted, the no-runtime evidence a launch rollback sends.
  • Handed-off resources: a cleanup_progress checkpoint may omit the workspace descriptor while none is recorded, so a provider can checkpoint before destroying a resource a launch handed off before binding a node. The periodic sweep leaves a target whose task has an active cleanup job to that job.
  • Profile snapshot: the recorded profile snapshot is sent on destroy, attach, inspect, resolve and the tunnel header, so a provider needs no process-local state.
  • Archive with a hung agentctl (plugin_remote only): the graceful agentctl stop and its lifecycle-lock wait are bounded (15 s or a third of the remaining archive budget), so a frozen agentctl no longer uses up the archive budget. A busy lock reports the stop as unconfirmed and the existing cleanup runs: drain uncertain, then the provider retains and may quarantine the node. Other runtimes keep the old path; no SDK or contract change.
  • Persistence: SQLite and PostgreSQL, with the same lock order (task row, then inventory lock, then execution row).
  • e2e-only: under the e2e mock profile a loopback or file:// repository remote is accepted, cloned without credentials and takes no Git credential lease, and agentctl's materialize endpoint accepts a host-less file:// locator, and the host forwards KANDEV_E2E_MOCK to the provider's agentctl; production is unchanged.
  • Destroy logging: every plugin executor destroy logs one structured line with the cleanup reason, drain outcome, claim, provider response and which unconfirmed condition fired. Every refused provider checkpoint logs its cause and the expected and received claim ids.
  • Design docs:
    • docs/specs/executors/requirements/remote-executor-plugin-native-workers.md
    • docs/specs/executors/system-design/remote-executor-plugin-native-workers.md
    • docs/plans/executor-plugin-native-workers/plan.md (work orders 00-08, 17)

Validation

  • go test -count=1 (lifecycle with -race) over ./internal/plugins/... ./cmd/plugin-fixture/... ./internal/task/... ./internal/agent/runtime/lifecycle/ ./internal/orchestrator/executor/ ./internal/gitcredentials/ ./internal/repoclone/.
    • At 8962b8bdeb, ./internal/agentctl/server/api/ ./internal/orchestrator/executor/ ./internal/task/service/ ./internal/repoclone/ ./internal/plugins/...: 7191 passed, 194 failed. ./internal/agent/runtime/lifecycle/ with -race: 5083 passed, 28 failed (counts include subtests).
    • Every one of those failures also fails on the merge base 173d1ef2d8 (the same 194 and 28 tests, none new): host-dependent worktree, Kubernetes-prepare, alias and archive-reclaim, and repository-selection tests.
  • cd pluginsdk && go test ./...: 164 passed, 0 failed.
  • PostgreSQL lock-order tests were run on PG 15 only. CI covers 16 and 18. No PostgreSQL server was available for the latest fixups: their new SQL tests (handoff delete and transfer guard, list-all handoffs) ran on SQLite, and their 2 PostgreSQL variants were skipped, not run.
  • Laptop rig (real Nomad agent and the private Nomad provider plugin, e2e mock agent), on 8962b8bdeb: smoke, archive during provisioning (5 runs), restart during provisioning, and loopback http and file:// repository runs all pass, in dev mode and with TLS, ACLs and a scoped token.
  • Live rig results above were recorded at f257db1d49. The node launch wrapper commit (2e9d23fad6) is not covered by those live runs; its checks are the unit tests listed in the node-wrapper section.
  • Head f257db1d49 (this PR), same rig in secure mode: that 9-scenario suite passes; a frozen-agentctl archive returns in about 40 s (it returned 500 after 180 s on 8962b8bd), the provider quarantines the node about 70 s after the freeze, a second launch is refused, operator readmission works and the next task runs; one real Codex agent turn (codex-acp) passes end to end. The dev-mode suite passed one commit earlier, at c100d78791.
  • New lifecycle tests for the bounded stop fail on 8962b8bdeb and pass on the head. The lifecycle package has the same 25 failing tests (host-dependent, e.g. macOS temp-dir and symlink paths) on 8962b8bdeb and on the head; none fail only on the head.

Node launch wrapper (added on this branch)

An operator-owned launch wrapper, set per node and not per task, can be required for every agentctl agent and utility launch. It is generic: core knows only a wrapper argv prefix and a required flag, and no sandbox product name appears in core.

  • Specs: docs/specs/agents/requirements/node-launch-wrapper.md and docs/specs/agents/system-design/node-launch-wrapper.md. The "no executor-level wrapper" exclusion in generic-launch-wrappers.md is narrowed to point at them.
  • Behaviour: the node wrapper is the outermost argv. A profile wrapper cannot clear or bypass it. A required node wrapper that is missing or invalid refuses the launch, with no unwrapped retry.
  • Checks: owner and mode checks on the file (read from the opened descriptor), on each ancestor directory and on argv[0], which must be an absolute path. The owner is set by flag, default is the agentctl process uid.
  • Coverage: managed agent process, one-shot args, interactive passthrough, ACP utility prompt and probe, OpenCode model listing, Codex app-server utility. Not covered on purpose: user shells, scripts and piped processes, MCP and helper processes.

Restart fix (follow-up message after a backend restart).
After a survivable backend shutdown, a follow-up message to a running plugin_remote task got no reply. Two causes:

  • Recovery asked the surviving agentctl for its delivery stream by session ID, but the peer's stream is named by incarnation and generation, so it answered 409 OWNER_MISMATCH and recovery failed every 60 s. Fixed by reading the peer's own stream for a legacy execution with no pinned stream, and checking session, incarnation and generation match (114a85bc8b).
  • The plugin_remote agentctl had no durable journal, so every recovered execution was legacy: published Running, never Ready, and a later launch hit cannot configure while agent is running. It now gets a journal under the provider-declared workspace, like Kubernetes (f7860bcaf9). The plugin's workspace removal deletes it with the environment. No SDK, proto or other-runtime change.

Live (local attach, mock agent, branch build): message right after restart replied at about +61 s (was none in 90 s); message 70 s after restart replied in 1.0 s (was session FAILED); default suite 9/9.

Limits: environments created before this change stay on the legacy path until recreated; the first reply waits for the 60 s provider retry; the durable-bootstrap re-create has no journal; only the mock agent was tried.

Worker-side API URL for the readiness probe (7530eb2735).
After provisioning, core probes the host API through the worker. On a remote worker (ssh node) the host URL 127.0.0.1:<kandev port> is not the host, so the probe failed with api_unreachable although the provider's forward was up. A provider can now return an optional worker_api_url (proto field 11, additive) and core probes that URL when set, else host_api_url as before.

  • Negotiated capability worker_api_url v1, requires api_url_forward. A provider that did not negotiate it and sends the field is refused; one that does not opt in behaves exactly as today.
  • Validation: http, loopback host (127.0.0.1, ::1, localhost), a port, no userinfo, path only /, no query or fragment. Only for provider_forwarded launches. Agentctl still forces the ping path.
  • Spec: REQ-EXECUTORS-NATIVE-WORKER-009 (new file, the native-workers file is at its size limit), plus capability-versioning and manifest docs. Open: whether sdk-release.json needs a change_kind bump.
  • Live (laptop, ssh rig, Nomad agent blocked from reaching Kandev): before, smoke failed api_unreachable; after, smoke replied in 8.1 s; unblocked control passed; default suite 9/9; ssh-restart pass. A patched plugin on the old core is refused at install (unknown capability), an unpatched plugin on the new core behaves as before.

Failed-launch drain fix (f70b18657d). Found on a real ssh node: agentctl died at startup and the node was quarantined. A provider checkpoints artifact_staging and bootstrapping from inside its provision call, so the launch's stored phase was ahead of the launch's local copy; the drain outcome was written with the stale phase and the store refused the move back to allocating, so the fallback cleanup_pending write ran and the provider never got the launch_failed destroy. The drain outcome is now recorded as cleanup_pending with the retained resource from the start. Tests: a failed provision after the provider checkpointed rolls back as absent; no handshake gives not_attempted, a handshake without a credential gives unreachable, a handshake with a credential gives drained. Local rig (agentctl exits 1 at startup): node no longer quarantined with this fix and the plugin fix; default suite 9/9, restart-message pass. Open: core's own handshake failing because agentctl died still reports unreachable.

Allocation directory token for the node wrapper (dd5007f704, 9f3c1ceb2a, 14853d2601). A node wrapper policy may use {{kandev.alloc_dir}}, a per-launch directory owned by the node's launcher and outside the agent's writable workspace. agentctl resolves it only from --node-launch-wrapper-alloc-dir (never from the environment or a request), requires an absolute, resolved, existing directory owned by the process owner, not group or world writable and not inside the workdir, and refuses a policy that uses the token without a value. REQ-AGENTS-NODEWRAP-006 AC .1 to .6; the node-launch-wrapper.v1 capability is unchanged (an older agentctl rejects the unknown flag and the token, so it never starts unwrapped). Tests cover token placement, each refusal, the value never coming from a request or the environment, and the unused-value case. Live (local rig): a recording wrapper received the directory; default suite 9/9, restart-message pass. Not proven: the real sandbox runner on a real node.

drain_receipt@2: a failed launch no longer quarantines a clean node (c7e889b40c, b815d79db3, f425b5a158, 9653ec926f, plus pinning tests 06755defc3). not_attempted now means the host never held an agentctl credential for this runtime. Gated by a new negotiated drain_receipt@2 (registry Min 1, Max 2; @1 hosts and records behave exactly as before; the version comes from the record's stored capabilities). Under @2: no handshake ever started plus no token gives not_attempted for any cleanup reason (was launch_failed only); a handshake that started but never produced a recorded credential (EOF, deadline, connection refused) also gives not_attempted, via two new sticky envelope fields credential_recorded (set in the same checkpoint that names the token secret, in both the launch and the durable-recovery paths) and handshake_rejected. A rejected handshake stays strict (unreachable), as do a recorded credential whose secret is unreadable, a non-durable provider after a started handshake, and any record without the new fields. The plugin still verifies the job, terminal allocations, node contract and worker state; only the drain is skipped. Preconditions are stated in the spec: agentctl starts no agent, shell or managed process before an authenticated request when a bootstrap nonce is configured (with none, authentication is off on loopback), and its only startup process is a code-server orphan sweep (pgrep, may SIGKILL an orphaned code-server host-wide; recorded as a known hazard, not changed). New spec files remote-executor-plugin-native-workers-drain-receipt-v2.md (requirements and system-design); the D1 table, AC 005.4 and capability-versioning are updated; the proto change is comment-only. Pinning tests prove the premises: only documented routes accept requests without a token, spawning routes start nothing unauthenticated, the wrapper is validated and never executed at startup, and the token reference is checkpointed before CreateInstance in both paths. Lifecycle failures equal base (the same 24 environment tests).

Decided on this branch:

  • Ancestor directories must be owned by the configured owner or root and not group or world writable. There is no sticky-bit exception.
  • argv[0] must be an absolute path, so PATH cannot redirect the wrapper.
  • The kandev backend's own passthrough runner refuses an agent launch when the agentctl it adopted advertises node-launch-wrapper.v1. Limit: a wrapper the backend was never told about is not detected, and that runner behaves as before (AC-AGENTS-NODEWRAP-005.4).

Not covered, follow-ups:

  1. ACP client terminals (commands an agent runs as tools). The agent process is wrapped, but these tool commands are not. Covering them needs the node wrapper plumbed into the ACP client and terminal manager.
  2. A host-side check of node-launch-wrapper.v1 before dispatch. The worker already fails closed, so this is a clearer error and defense in depth, not a bypass.

Tests: new tests in launchwrapper, process (node_wrapper_test.go), utility (node_wrapper_test.go), api and cmd/agentctl, including the composition-order test TestNodeWrapperIsFirstArgvOfSessionCommand. Run locally: -race passes for the new tests. TestGenericWrapperPrepare fails on this branch and on f257db1d49 alike (macOS /var vs /private/var), so it is pre-existing and not changed here.

Commits: the spec is a separate commit. The code and tests are one commit (26 files), because the per-commit lint needs the implementation to compile with the tests. The PR is one logical change, but it is large; splitting is possible if reviewers ask.

Possible Improvements

Medium risk: persistence and lock-order changes. Recorded secrets are revealed to the provider on every attach, inspect, resolve and tunnel-open call, because the provider needs its cluster credential each time. A lease-scoped credential would narrow that.

Checklist

  • If I do not have repository write access and this is a large architectural change, I discussed the direction in a linked issue before opening this PR.
  • This PR contains one logical change; unrelated work is split into separate PRs.
  • I have performed a self-review of my code.
  • I have manually tested my changes and they work as expected.
  • My changes have tests that cover the new functionality and edge cases.
  • If my change touches UI files (apps/web/), I have added or updated Playwright e2e tests in apps/web/e2e/ and verified them with make test-e2e.
  • I checked whether this affects public docs in docs/public/** and updated them or noted why no docs change is needed.

View guided diff

Preview Environment

URL https://kandev-pr-4934-bwo7.sprites.app
Commit f274c85
Agent Mock agent

Updates automatically on each push. Destroyed when the PR is closed.

@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 315 files, which is 165 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: QUIET
  • Plan: Advanced
  • Run ID: 0595d46e-113f-47d5-8044-4250b67b8b5b
Commits

Reviewing files that changed from the base of the PR and between 997bc54 and 5b7632a.

Files ignored due to path filters: 2
  • pluginsdk/proto/kandev/plugin/v1/plugin.pb.go is excluded by !**/*.pb.go
  • pluginsdk/proto/kandev/plugin/v1/plugin_grpc.pb.go is excluded by !**/*.pb.go
Files selected for processing: 315
  • apps/backend/cmd/agentctl/main.go
  • apps/backend/cmd/agentctl/node_launch_wrapper.go
  • apps/backend/cmd/agentctl/node_launch_wrapper_startup_test.go
  • apps/backend/cmd/agentctl/node_launch_wrapper_test.go
  • apps/backend/cmd/plugin-fixture/executor_provider.go
  • apps/backend/cmd/plugin-fixture/executor_provider_cleanup_checkpoint_test.go
  • apps/backend/cmd/plugin-fixture/executor_provider_drain_evidence_test.go
  • apps/backend/cmd/plugin-fixture/executor_provider_test.go
  • apps/backend/cmd/plugin-fixture/fixture-package/manifest.yaml
  • apps/backend/internal/agent/runtime/agentctl/control.go
  • apps/backend/internal/agent/runtime/agentctl/control_runtime_drain_test.go
  • apps/backend/internal/agent/runtime/agentctl/control_test.go
  • apps/backend/internal/agent/runtime/agentctl/endpoint_transport.go
  • apps/backend/internal/agent/runtime/agentctl/endpoint_tunnel_test.go
  • apps/backend/internal/agent/runtime/crash_barrier.go
  • apps/backend/internal/agent/runtime/lifecycle/agentctl_lifecycle_bounded.go
  • apps/backend/internal/agent/runtime/lifecycle/crash_barrier.go
  • apps/backend/internal/agent/runtime/lifecycle/crash_barrier_interleaving_test.go
  • apps/backend/internal/agent/runtime/lifecycle/crash_barrier_test.go
  • apps/backend/internal/agent/runtime/lifecycle/delivery_stream_legacy_recovery_test.go
  • apps/backend/internal/agent/runtime/lifecycle/delivery_stream_preflight.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_backend.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_agent_env_e2e_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_archive_drain_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_archive_provision_repo_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_callbacks.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_capability_gate.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_capability_gate_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_claim_checkpoint.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_claim_checkpoint_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_claim_mismatch_log_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_claim_settlement_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_cleanup_sweep.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_cleanup_sweep_fence_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_cleanup_sweep_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_cleanup_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_destroy_log.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_destroy_log_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_destroy_tunnels_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_drain.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_drain_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_drain_v2_credential_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_drain_v2_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_durable_bootstrap.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_durable_bootstrap_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_durable_token_resume_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_e2e_local_launch_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_early_checkpoint_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_failed_provision_drain_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_handoff_checkpoint_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_handoff_settlement_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_host_api_probe.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_host_api_probe_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_inflight_launch_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_journal_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_launch_cleanup_claim_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_launch_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_profile_contract.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_profile_contract_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_provider_refusal_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_recorded_profile_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_recover_version_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_recovery.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_recovery_progress_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_recovery_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_repository_intent.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_repository_intent_helpers_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_repository_intent_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_repository_inventory_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_retained.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_rollback_drain_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_rollback_evidence_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_rotated_provision_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_state_budget.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_state_budget_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_status.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_token_ref_order_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_tunnel_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_unrecorded.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_unrecorded_handoff_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_validation.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_worker_api_url.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_worker_api_url_test.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_workspace.go
  • apps/backend/internal/agent/runtime/lifecycle/executor_plugin_workspace_test.go
  • apps/backend/internal/agent/runtime/lifecycle/manager.go
  • apps/backend/internal/agent/runtime/lifecycle/manager_interaction.go
  • apps/backend/internal/agent/runtime/lifecycle/manager_launch.go
  • apps/backend/internal/agent/runtime/lifecycle/manager_plugin_executor_inventory.go
  • apps/backend/internal/agent/runtime/lifecycle/manager_stop_plugin_hung_agentctl_test.go
  • apps/backend/internal/agent/runtime/lifecycle/remote_recovery_plugin.go
  • apps/backend/internal/agent/runtime/lifecycle/remote_recovery_plugin_stop_test.go
  • apps/backend/internal/agent/runtime/lifecycle/remote_recovery_stop.go
  • apps/backend/internal/agent/runtime/lifecycle/types.go
  • apps/backend/internal/agent/settings/cliflags/tokenise.go
  • apps/backend/internal/agent/settings/cliflags/tokenise_test.go
  • apps/backend/internal/agentctl/server/api/control_server.go
  • apps/backend/internal/agentctl/server/api/host_api_probe.go
  • apps/backend/internal/agentctl/server/api/host_api_probe_test.go
  • apps/backend/internal/agentctl/server/api/identity.go
  • apps/backend/internal/agentctl/server/api/node_wrapper_capability_test.go
  • apps/backend/internal/agentctl/server/api/prelaunch_surface_test.go
  • apps/backend/internal/agentctl/server/api/runtime_drain.go
  • apps/backend/internal/agentctl/server/api/runtime_drain_test.go
  • apps/backend/internal/agentctl/server/api/server.go
  • apps/backend/internal/agentctl/server/api/workspace_materialize.go
  • apps/backend/internal/agentctl/server/api/workspace_materialize_test.go
  • apps/backend/internal/agentctl/server/config/config.go
  • apps/backend/internal/agentctl/server/instance/manager.go
  • apps/backend/internal/agentctl/server/instance/manager_partial_shutdown_test.go
  • apps/backend/internal/agentctl/server/instance/manager_runtime_drain_test.go
  • apps/backend/internal/agentctl/server/process/interactive_lifecycle.go
  • apps/backend/internal/agentctl/server/process/interactive_runner.go
  • apps/backend/internal/agentctl/server/process/launch_wrapper.go
  • apps/backend/internal/agentctl/server/process/manager.go
  • apps/backend/internal/agentctl/server/process/node_wrapper_test.go
  • apps/backend/internal/agentctl/server/process/passthrough.go
  • apps/backend/internal/agentctl/server/utility/acp_executor.go
  • apps/backend/internal/agentctl/server/utility/codex_app_server.go
  • apps/backend/internal/agentctl/server/utility/handler.go
  • apps/backend/internal/agentctl/server/utility/launch_wrapper.go
  • apps/backend/internal/agentctl/server/utility/node_wrapper_test.go
  • apps/backend/internal/agentctl/server/utility/probe_failure_log.go
  • apps/backend/internal/agentctl/server/utility/types.go
  • apps/backend/internal/agentctl/types/host_api_probe.go
  • apps/backend/internal/agentctl/types/runtime_drain.go
  • apps/backend/internal/auth/httpmw/middleware.go
  • apps/backend/internal/auth/httpmw/middleware_test.go
  • apps/backend/internal/backendapp/adapters.go
  • apps/backend/internal/backendapp/agents.go
  • apps/backend/internal/backendapp/e2e_crash_barriers.go
  • apps/backend/internal/backendapp/e2e_crash_barriers_test.go
  • apps/backend/internal/backendapp/helpers.go
  • apps/backend/internal/backendapp/main.go
  • apps/backend/internal/backendapp/passthrough_node_wrapper.go
  • apps/backend/internal/backendapp/passthrough_node_wrapper_test.go
  • apps/backend/internal/backendapp/runtime_probe_wiring.go
  • apps/backend/internal/backendapp/runtime_probe_wiring_test.go
  • apps/backend/internal/backendapp/types.go
  • apps/backend/internal/backendapp/workspace_source_materializer_e2e_local_test.go
  • apps/backend/internal/backendapp/workspace_source_materializer_sources.go
  • apps/backend/internal/common/launchwrapper/alloc_dir.go
  • apps/backend/internal/common/launchwrapper/alloc_dir_test.go
  • apps/backend/internal/common/launchwrapper/alloc_dir_unix.go
  • apps/backend/internal/common/launchwrapper/alloc_dir_windows.go
  • apps/backend/internal/common/launchwrapper/node.go
  • apps/backend/internal/common/launchwrapper/node_ancestor_test.go
  • apps/backend/internal/common/launchwrapper/node_file_unix.go
  • apps/backend/internal/common/launchwrapper/node_file_windows.go
  • apps/backend/internal/common/launchwrapper/node_test.go
  • apps/backend/internal/common/launchwrapper/prepare.go
  • apps/backend/internal/common/launchwrapper/spec.go
  • apps/backend/internal/gateway/websocket/proxy_host_tunnel_test.go
  • apps/backend/internal/gitcredentials/identity.go
  • apps/backend/internal/gitcredentials/identity_e2e_local.go
  • apps/backend/internal/gitcredentials/identity_e2e_local_test.go
  • apps/backend/internal/orchestrator/executor/executor.go
  • apps/backend/internal/orchestrator/executor/executor_credentials.go
  • apps/backend/internal/orchestrator/executor/executor_credentials_e2e_local_test.go
  • apps/backend/internal/orchestrator/executor/executor_e2e_local_repo_spec_test.go
  • apps/backend/internal/orchestrator/executor/executor_execute.go
  • apps/backend/internal/orchestrator/executor/repo_spec_position_test.go
  • apps/backend/internal/plugins/executor_capability_echo.go
  • apps/backend/internal/plugins/executor_capability_echo_test.go
  • apps/backend/internal/plugins/executor_capability_gate_service_test.go
  • apps/backend/internal/plugins/executor_conformance_test.go
  • apps/backend/internal/plugins/executor_lifecycle_guard.go
  • apps/backend/internal/plugins/executor_lifecycle_guard_test.go
  • apps/backend/internal/plugins/executor_providers.go
  • apps/backend/internal/plugins/executor_providers_test.go
  • apps/backend/internal/plugins/executor_state_readability_test.go
  • apps/backend/internal/plugins/executor_state_writable.go
  • apps/backend/internal/plugins/executor_state_writable_test.go
  • apps/backend/internal/plugins/executor_tunnel.go
  • apps/backend/internal/plugins/executor_tunnel_test.go
  • apps/backend/internal/plugins/executortunnel/fake_stream_test.go
  • apps/backend/internal/plugins/executortunnel/goleak_test.go
  • apps/backend/internal/plugins/executortunnel/registry.go
  • apps/backend/internal/plugins/executortunnel/registry_test.go
  • apps/backend/internal/plugins/handlers.go
  • apps/backend/internal/plugins/handlers_host_capabilities.go
  • apps/backend/internal/plugins/handlers_host_capabilities_test.go
  • apps/backend/internal/plugins/hostcaps/registry.go
  • apps/backend/internal/plugins/hostcaps/registry_test.go
  • apps/backend/internal/plugins/hostcaps/testdata/retired.golden
  • apps/backend/internal/plugins/hostcaps/testhelpers_test.go
  • apps/backend/internal/plugins/manifest/clone.go
  • apps/backend/internal/plugins/manifest/executor_provider.go
  • apps/backend/internal/plugins/manifest/executor_provider_capabilities.go
  • apps/backend/internal/plugins/manifest/executor_provider_capabilities_test.go
  • apps/backend/internal/plugins/manifest/executor_provider_destroy_timeout_test.go
  • apps/backend/internal/plugins/manifest/executor_provider_profile_keywords_test.go
  • apps/backend/internal/plugins/manifest/executor_provider_runtime_api_url_test.go
  • apps/backend/internal/plugins/manifest/executor_provider_workspace_preparation_test.go
  • apps/backend/internal/plugins/manifest/manifest.go
  • apps/backend/internal/plugins/profileschema/profileschema.go
  • apps/backend/internal/plugins/profileschema/profileschema_test.go
  • apps/backend/internal/plugins/profilesnapshot/snapshot.go
  • apps/backend/internal/plugins/profilesnapshot/snapshot_test.go
  • apps/backend/internal/plugins/service.go
  • apps/backend/internal/plugins/service_install.go
  • apps/backend/internal/plugins/workspacedescriptor/within_test.go
  • apps/backend/internal/plugins/workspacedescriptor/workspacedescriptor.go
  • apps/backend/internal/plugins/workspacedescriptor/workspacedescriptor_test.go
  • apps/backend/internal/repoclone/clone.go
  • apps/backend/internal/repoclone/e2e_local.go
  • apps/backend/internal/repoclone/e2e_local_test.go
  • apps/backend/internal/runtimeprobe/registry.go
  • apps/backend/internal/runtimeprobe/registry_test.go
  • apps/backend/internal/task/models/executor_provider.go
  • apps/backend/internal/task/models/models.go
  • apps/backend/internal/task/models/unrecorded_plugin_executor.go
  • apps/backend/internal/task/recoveryclaim/claim.go
  • apps/backend/internal/task/repository/sqlite/base.go
  • apps/backend/internal/task/repository/sqlite/base_schema.go
  • apps/backend/internal/task/repository/sqlite/errors.go
  • apps/backend/internal/task/repository/sqlite/executor_plugin_inventory.go
  • apps/backend/internal/task/repository/sqlite/executor_plugin_inventory_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_claim_checkpoint.go
  • apps/backend/internal/task/repository/sqlite/plugin_claim_checkpoint_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_claim_checkpoint_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_claim_crash_recovery_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_claim_supersede_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_cleanup_request.go
  • apps/backend/internal/task/repository/sqlite/plugin_cleanup_request_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_cleanup_request_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_envelope_lock_order_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_inventory_phase_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_publish_recovered.go
  • apps/backend/internal/task/repository/sqlite/plugin_publish_recovered_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_publish_recovered_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_secret_ref_checkpoint_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_state_readability.go
  • apps/backend/internal/task/repository/sqlite/plugin_state_readability_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_state_readability_typed_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_workspace_descriptor.go
  • apps/backend/internal/task/repository/sqlite/plugin_workspace_descriptor_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/plugin_workspace_descriptor_test.go
  • apps/backend/internal/task/repository/sqlite/task_environment.go
  • apps/backend/internal/task/repository/sqlite/unrecorded_plugin_executor.go
  • apps/backend/internal/task/repository/sqlite/unrecorded_plugin_executor_guard_test.go
  • apps/backend/internal/task/repository/sqlite/unrecorded_plugin_executor_postgres_test.go
  • apps/backend/internal/task/repository/sqlite/unrecorded_plugin_executor_test.go
  • apps/backend/internal/task/service/e2e_local_remote.go
  • apps/backend/internal/task/service/e2e_local_remote_test.go
  • apps/backend/internal/task/service/e2e_local_task_creation_test.go
  • apps/backend/internal/task/service/executor_provider_catalog.go
  • apps/backend/internal/task/service/executor_provider_profile_contract.go
  • apps/backend/internal/task/service/executor_provider_profile_contract_test.go
  • apps/backend/internal/task/service/executor_provider_profile_launch_test.go
  • apps/backend/internal/task/service/executor_provider_profiles.go
  • apps/backend/internal/task/service/service_tasks.go
  • apps/backend/internal/task/service/service_workspace_sources.go
  • apps/web/components/settings/plugin-executor-profile-defaults.test.tsx
  • apps/web/components/settings/plugin-executor-profile-fields.tsx
  • apps/web/e2e/helpers/plugin-executor-profile.ts
  • apps/web/e2e/tests/settings/mobile-plugin-executor-profiles.spec.ts
  • apps/web/e2e/tests/settings/plugin-executor-profiles.spec.ts
  • apps/web/lib/plugins/executor-profile-schema.test.ts
  • apps/web/lib/plugins/executor-profile-schema.ts
  • apps/web/src/locales/en/executors.json
  • apps/web/src/locales/ja/executors.json
  • apps/web/src/locales/ko/executors.json
  • apps/web/src/locales/pseudo/executors.json
  • apps/web/src/locales/pt-pt/executors.json
  • apps/web/src/locales/zh-cn/executors.json
  • apps/web/src/locales/zh-hk/executors.json
  • apps/web/src/locales/zh-tw/executors.json
  • docs/decisions/2026-09-26-remote-executor-plugin-boundary.md
  • docs/decisions/2026-10-28-executor-plugin-native-worker-decisions.md
  • docs/plans/executor-plugin-native-workers/plan.md
  • docs/plans/executor-plugin-native-workers/task-00-record-owner-decisions.md
  • docs/plans/executor-plugin-native-workers/task-01-capability-registry-versioning.md
  • docs/plans/executor-plugin-native-workers/task-02-workspace-descriptor.md
  • docs/plans/executor-plugin-native-workers/task-03-destroy-recover-checkpoints.md
  • docs/plans/executor-plugin-native-workers/task-04-drain-receipt.md
  • docs/plans/executor-plugin-native-workers/task-05-durable-bootstrap-publication.md
  • docs/plans/executor-plugin-native-workers/task-06-host-tunnel-api-forwarding.md
  • docs/plans/executor-plugin-native-workers/task-07-repository-intent.md
  • docs/plans/executor-plugin-native-workers/task-08-crash-barrier.md
  • docs/plans/executor-plugin-native-workers/task-10-admission-context.md
  • docs/plans/executor-plugin-native-workers/task-11-pending-placement.md
  • docs/plans/executor-plugin-native-workers/task-12-provider-operations-maintenance.md
  • docs/plans/executor-plugin-native-workers/task-13-launch-policy.md
  • docs/plans/executor-plugin-native-workers/task-14-preparation-hints.md
  • docs/plans/executor-plugin-native-workers/task-16-fixture-provider-proof.md
  • docs/plans/executor-plugin-native-workers/task-17-profile-contract.md
  • docs/public/plugins-manifest.md
  • docs/specs/agents/README.md
  • docs/specs/agents/requirements/generic-launch-wrappers.md
  • docs/specs/agents/requirements/node-launch-wrapper.md
  • docs/specs/agents/system-design/node-launch-wrapper.md
  • docs/specs/executors/README.md
  • docs/specs/executors/requirements/remote-executor-plugin-admission.md
  • docs/specs/executors/requirements/remote-executor-plugin-capability-versioning.md
  • docs/specs/executors/requirements/remote-executor-plugin-native-workers-drain-receipt-v2.md
  • docs/specs/executors/requirements/remote-executor-plugin-native-workers-recovery.md
  • docs/specs/executors/requirements/remote-executor-plugin-native-workers-worker-api-url.md
  • docs/specs/executors/requirements/remote-executor-plugin-native-workers.md
  • docs/specs/executors/requirements/remote-executor-plugin-operations-migration.md
  • docs/specs/executors/requirements/remote-executor-plugin-placement.md
  • docs/specs/executors/requirements/remote-executor-plugin-profile-contract.md
  • docs/specs/executors/requirements/remote-executor-plugins.md
  • docs/specs/executors/system-design/remote-executor-plugin-admission.md
  • docs/specs/executors/system-design/remote-executor-plugin-capability-versioning.md
  • docs/specs/executors/system-design/remote-executor-plugin-native-workers-drain-receipt-v2.md
  • docs/specs/executors/system-design/remote-executor-plugin-native-workers.md
  • docs/specs/executors/system-design/remote-executor-plugin-operations-migration.md
  • docs/specs/executors/system-design/remote-executor-plugin-profile-contract.md
  • pluginsdk/executor_capabilities_test.go
  • pluginsdk/executor_provider.go
  • pluginsdk/executor_tunnel.go
  • pluginsdk/executor_tunnel_test.go
  • pluginsdk/proto/kandev/plugin/v1/plugin.proto

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

nova28 and others added 29 commits October 29, 2026 08:04
Add requirements, system design, plan and work orders for opt-in host
capabilities (workspace descriptor, repository intent, host-owned tunnel,
drain receipt, destroy/recover checkpoints, durable bootstrap, crash
barrier) so a worker-pool provider can ship as a plugin.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ty versioning first

Add capability negotiation and versioning, shared environments, admission,
pending placement, provider operations, launch policy, extensible preparation,
and migration requirements and designs. Reorder the plan with M7 first and add
work orders 01 and 09-15.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…n spec

Add the profile schema, typed values, and secret references requirements and
design, the min_kandev_version rule, the state budget, and the refused
provision outcome. Add work order 17 and update the plan and fixture proof.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in capabilities

Map state schema onto the existing state-version mechanism, add the envelope byte budget and
state_too_large rule, define shared-owner rows, admission order, cancel versus completion,
migration source and routes, secret references on main, and align the work orders.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in capabilities

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in capabilities

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…pecs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in capabilities

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…le (round 5)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…findings

Cut M8 (MIGRATION-001, work order 15) to Out of scope, follow-up card a8546b99. Resume keeps its
reservation and goes through attachRetainedPluginExecutor with the same gates; deny refuses, hook
outage resumes. Close L1, L2, L4, L6-L8, L12-L16.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…a follow-up card

Cut resource classes, labels and provider-reported admission values to card
11c92feb. Resume never takes the scope lock; placement_failed/timeout recovery
specified against recoverPluginExecutor; designs and work orders aligned.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…sume fence findings

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…yntax

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tion echo

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…utor state

The lifecycle confirms the manifest, registry and accepted echo before any
provider call on first launch and on retained attach, pins the negotiated set
into the plugin_executor envelope, refuses oversized provider state with
state_too_large (cleaning the resource), and reports the affected environment
count when an upgrade would strand recorded state.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…admission

Add the per-installation readability lock shared by upgrade activation and
the admission checkpoint, the writable checker wired from the plugins
service, and the admin read of the host capability registry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eferences

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… environment

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…th sequence fencing

A provider that negotiates destroy_checkpoints can checkpoint under the active
cleanup or recovery claim. The repository validates the claim, the per-claim
sequence and the envelope revision in one transaction, so a released claim, a
successor claim or a competing checkpoint changes nothing. Settlement to absent
now also requires a stored checkpoint of the claim.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lugin environment

Add the agentctl runtime seal-and-drain endpoint with claim-keyed receipts,
the drain_receipt capability with drain_outcome and a receipt on the destroy
request, destroy_timeout_seconds in the manifest, and an idle_timeout_seconds
bootstrap field so a drainable runtime keeps its idle reaper off. Only task
teardown cleanup drains; ordinary stop, backend shutdown and plugin disable
never seal the runtime.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ry claim

Guarded, idempotent flip of a creating environment to ready on both
dialects: the held claim, session, generation, runtime identity, phase,
workspace descriptor, repository inventory and revision are re-checked in
the publishing transaction, and a mismatch publishes nothing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…er the recovery claim

Repeat the agentctl handshake with the stored one-time nonce, adopt and
publish the environment when it is accepted, and clean it up as
bootstrap_consumed when agentctl rejects the nonce.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…API URLs

Adds the runtime_api_url manifest key (host_public default, provider_forwarded
with api_url_forward), the unauthenticated read-only runtime ping route backed
by a one-time probe token, the agentctl probe-host-api control route, and a
forwarded-launch probe that rolls the environment back as api_unreachable.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…uild record

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A lease transport of host_tunnel makes Kandev open bidirectional streams
to the provider, bound to installation, environment, ownership generation
and one allowed port, acknowledged before any request byte flows.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…agent launch

Includes the tests, which were written and seen failing first but cannot
pass the per-commit package lint without the implementation.
@nova28
nova28 deployed to opencode-review-trusted October 30, 2026 10:59 — with GitHub Actions Active
…nd passthrough runner closed

Tests trust t.TempDir() through an unexported, test-only boundary seam; the
production boundary is the filesystem root.
@nova28
nova28 deployed to opencode-review-trusted October 30, 2026 11:31 — with GitHub Actions Active
… legacy remote execution

A recovered execution on an agentctl without retained storage pins no stream,
so the replay preflight asked for the session ID while the peer names its
stream <incarnation>:g<generation>. The peer answered OWNER_MISMATCH, the
attach failed on every retry and the session stayed behind the recovery guard.
Ask the peer for its current stream and verify the owner identity instead.
…l under the environment workspace

Without a journal the peer is a legacy delivery peer, so a recovered execution
stays Running and never takes the durable Ready route after a backend restart.
Place the journal under the provider-declared workspace, as the SSH and
Kubernetes executors do, keyed by the session owner.
@nova28
nova28 deployed to opencode-review-trusted October 30, 2026 13:25 — with GitHub Actions Active
Adds the worker_api_url capability and an optional worker_api_url field on
the provision resource descriptor. A remote worker cannot reach the host's
own listener, so the reachability probe uses the loopback forward the
provider reports when it opted in.
@nova28
nova28 deployed to opencode-review-trusted October 30, 2026 14:29 — with GitHub Actions Active
…tored phase

A provider checkpoints artifact_staging and bootstrapping from inside its
provision call, so the launch's own copy of the inventory phase can be behind
the stored row. The rollback of a failed launch recorded its drain outcome with
that stale phase, the store refused the move back to allocating, and the
rollback gave up with the cleanup pending. The environment reset that retried
it later had no handshake to prove nothing ran, reported the runtime as
unreachable, and the provider quarantined a healthy node.

Record the drain outcome as cleanup_pending, which every live phase may reach.
@nova28
nova28 deployed to opencode-review-trusted October 31, 2026 00:40 — with GitHub Actions Active
@nova28
nova28 deployed to opencode-review-trusted October 31, 2026 01:53 — with GitHub Actions Active
…dering

Add evidence tests for the launch-failure work, with no behaviour change:
- every agentctl control and instance route outside the documented
  exemptions rejects a request without the token
- no instance, process manager or launched process exists before an
  authenticated create, and startup launches exactly the orphan sweep
- loading a node launch wrapper reads it and never executes it
- the agentctl token reference is checkpointed before CreateInstance on
  the launch and durable-bootstrap recovery paths
Version 2 reuses not_attempted for 'the host never held an agentctl
credential for this runtime'. The registry accepts versions 1 to 2, a
manifest names exactly one, and version 1 is unchanged. The proto comment
and the contract documents carry the stated preconditions and the risks.
…eceipt version 2

When no credential is available and no handshake was ever started, a record
that pinned drain_receipt@2 sends not_attempted for every cleanup reason.
The version is read from the stored record. Version 1 is unchanged, and the
fixture provider accepts the wider meaning only when it declares version 2.
…tial

Adds the sticky envelope fields credential_recorded, set in the checkpoint
that names the token secret in both the launch and the recovery path, and
handshake_rejected. Under drain_receipt@2 with durable_bootstrap a started
handshake that never produced a recorded credential and was not rejected is
not_attempted; a rejected handshake, a recorded credential with an
unreadable secret, a non-durable provider and a record without the field
stay unreachable.
@nova28
nova28 deployed to opencode-review-trusted October 31, 2026 02:53 — with GitHub Actions Active
…restart

A plugin_remote session waiting in the pending recovery queue whose agentctl
is gone returned an empty recovery outcome, so the archive stop failed and
retried forever and DestroyTaskEnvironment never ran. A cleanup stop now
completes and leaves the stored record to the environment cleanup, which
drains from the record (unreachable when a credential was recorded).
@nova28
nova28 deployed to opencode-review-trusted October 31, 2026 07:27 — with GitHub Actions Active
# Conflicts:
#	apps/backend/internal/agentctl/server/process/manager.go
#	pluginsdk/proto/kandev/plugin/v1/plugin.pb.go
@nova28
nova28 deployed to opencode-review-trusted October 31, 2026 13:49 — with GitHub Actions Active
@nova28
nova28 deployed to opencode-review-trusted October 31, 2026 15:13 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
opencode-review-trusted — f274c85f Deployed Oct 31, 2026 by nova28 via pr-walkthrough-generate #9503
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

big Pull request changes 51 or more application files safe-to-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant