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.
Repository handoff, plugin content inspection and lifecycle receipts preserve separate repository, Codex and application ownership.
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.