ADR 026 Plugin Repository Handoff and Integrity - joeroberts/release-radar GitHub Wiki

  • ID: ADR-026
  • Status: Accepted
  • Date: 2026-08-28
  • Date meaning: Original dated decision/source section; extraction recorded 2026-10-02.
  • Implementation: No implementation information was recorded in the source status; tickets own progress.
  • Approval qualification: Recorded accepted decision; dated delivery conditions are historical, not new execution authorization.

Context

Repository handoff, plugin content inspection and lifecycle receipts preserve separate repository, Codex and application ownership.

Decision

The installed skill may direct an owner-authorized Codex task to preserve or create applicable repository AGENTS.md guidance and the minimum durable delivery ledger during requested initialization. Those are Codex repository writes, not app or lifecycle-helper writes. The same task uses only existing typed Release Radar MCP mutations for the corresponding delivery state; Release Radar remains the sole SQLite writer and returns the mutation's audit result. Completion reads repository files back and requires that successful audited MCP result. This does not authorize an MCP repository-read operation, app-side repository authority, a new MCP operation, observer transport, polling, synchronization/reconciliation, HTTP, or multi-user behavior.

For Task 1's corrected composition proof, the exact recognized legacy entry remains untouched. The proof first requires exact absence for bundled key release_radar, then installs the derived plugin and requires one fresh task to emit a machine-verifiable mcp_tool_call for that server; cleanup removes only the temporary plugin and marketplace and re-verifies exact bundled-key absence. Earlier accepted Task 1 evidence already proves exact legacy removal, pinned absence, and restoration through supported CLI vectors. For the product's first managed Install, the helper first accepts bundled key release_radar as absent or as the exact enabled STDIO entry with no disabled reason, the packaged AgentTools command, empty arguments, null working directory, and empty or null environment fields. Contradictory fields fail closed. It classifies both direct and legacy entries before mutation, then immediately rereads and removes only that exact direct entry through the supported CLI, verifies absence, and restores it on a later failure only if this operation removed it and the key remains absent. Changed or unrecognized direct state fails closed. The helper then retains the operation-local initial absent or exact-legacy release-radar observation, installs the managed plugin, and verifies bundled server release_radar. An initially absent entry proceeds directly to the final bundled-server postcondition. An initially exact legacy entry is immediately reread and removed through the supported CLI only when the fresh result still exactly matches, after which bundled server release_radar is verified again. An absent or changed fresh legacy result avoids removal and rolls back attempt-owned state without restoration. After the operation issues removal, a later failure may restore the recognized entry. Only the final bundled-server verification permits the app to persist managedInstalled. Later lifecycle operations neither repeat the migration nor recreate the legacy or direct entry.

Task 1 feasibility composes two existing Release Radar boundaries instead of creating a second lifecycle service. The lifecycle lane uses only the verified official Codex CLI and the accepted focused controller evidence. The runtime lane reuses the packaged ReleaseRadarAgentTools, existing signed ReleaseRadarBridgeAgent, existing typed bridge contracts, and a temporary app-owned DeliveryStore.

Task 1 creates no lifecycle-helper target, feasibility Mach service, generated harness application, alternate XPC protocol, process reconciler, or tracing framework. The production lifecycle helper's SMAppService registration, approval, authenticated four-method XPC surface, and cleanup remain blocking Product Task 2 acceptance requirements after that helper exists; they are not prerequisites for proving the v1 CLI and existing local runtime boundaries.

The official CLI is the sole mutation owner but does not expose installed content integrity. For status only, production obtains the effective user's home with getpwuid_r(geteuid()), never $HOME, and combines it with one strict SemVer component from the targeted CLI version to construct only ~/.codex/plugins/cache/release-radar/release-radar/<version>. It walks fixed components through descriptor-relative no-follow opens, verifies each file is stable and regular across the complete read, and reads exactly the recognized legacy or current declared plugin-file inventory to compute the normalized digest. Only an internal test seam accepts a derived home. It never writes Codex state, reads Codex configuration, enumerates other plugins, or returns, logs, or persists a home or cache path.

The app persists owner intent and verified lifecycle receipts through its existing sole-writer store. It does not treat those receipts as Codex configuration. Automatic update is permitted only for an intact, recognized, previously managed installation. Never-installed, removed, absent, modified, conflicted, or unknown state requires no automatic mutation. Modified content requires an explicit confirmed Reinstall.

The accepted runtime delivery boundary is unchanged:

Codex -> ReleaseRadarAgentTools -> signed XPC bridge
      -> Release Radar app -> app-owned SQLite

The lifecycle helper is not callable by Codex and cannot request or perform a ticket transition.

⚠️ **GitHub.com Fallback** ⚠️