Runtime architecture · owner initiated · macOS v1

In-conversation self-update

A user asks an Agent in OpenProgram to modify OpenProgram. The Agent edits, tests, and commits in an isolated worktree; a worker-independent controller builds and installs the complete App. After the new worker starts, it automatically creates an acceptance task to check the original objective. System-check or goal-acceptance failure restores the old App.

Core constraint: The worker being replaced cannot complete its own replacement. Persist handoff and pause checkpoints for other tasks before restart, then create a new acceptance task with provenance. After verification, resume compatible tasks from checkpoints and create a follow-up for the initiating session.
Platform
Default macOS App
/Applications/OpenProgram.app · :18100 · ~/.openprogram
Candidate
Local Git commit
Isolated worktree; dirty candidates prohibited
Activation
Complete App bundle
No wheel/asar incremental paths in v1
Concurrency
One active update
Installation lock + update id + idempotent recovery

Question

How can an owner authorize framework changes in chat, followed automatically by build, stopping the old instance, App replacement, restart, acceptance Agent execution, and failure handling?

Audience

Runtime, Desktop, Job/DAG, permission, and local-release maintainers.

Exclusions

Windows/Linux, remote stable Releases, unauthorized background edits, multi-machine deployment, and unlimited automatic repair loops.

01

Current implementation and gaps

The self-update control protocol, post-restart acceptance, controlled repair, and status UI are in the combined candidate. Full combined checks and default-App acceptance for success, pre-activation failure, and post-activation rollback remain release requirements; source existence does not prove an installed working feature.

Reused module
Original behavior
Self-update responsibility
Agent edits and worktrees
Owner sessions can use file, shell, and worktree tools; permission, sandbox, and read-before-edit rules constrain writes.
Add a dedicated self_update_prepare accepting committed candidates only; generic shell access must not implicitly grant deployment authority.
Complete App build
package-and-install-app.sh already builds the runtime and Electron App and runs packaged smoke checks.
Delegate build and activation to a one-shot supervisor; do not stop the current worker during build.
Transactional installation and recovery
install-app.sh already validates candidates, preserves previous.app, restores the old App on failure, and restores original App/worker/launchd state.
Delay deleting previous.app until post-restart system checks and Agent goal acceptance both complete.
Source upgrade probe
openprogram upgrade already supports cold start, doctor, restart, and expected-revision checks.
Extract a shared probe; use a local immutable commit instead of origin/main.
Session / Job persistence
Session history persists; ordinary running turns become interrupted and running Jobs become worker_lost on restart.
Do not reuse a running Job. Create a new verification Job in the new worker from the update record.
Scheduler / JobRunner
Scheduler handles time-triggered once, recurring, and monitor user tasks; JobRunner provides durable Jobs, concurrency limits, and resource governance.
Self-update creates no Scheduler task. Startup recovery claims pending acceptance records, then submits a one-shot verification Job through JobRunner.
Updater detection
openprogram/updater/ distinguishes managed release, source checkout, and unsupported installations, and prohibits applying updates directly during worker startup.
Reuse installation detection; startup hooks only inspect, claim, and schedule acceptance. External supervisor still replaces the App.
Cross-restart handoff
self_update/ stores independent requests, state, controller, and acceptance evidence without reusing upgrade-state.
Persist turn-end release, startup claim, stable verification Job, result delivery to the original session, and Desktop reopen. Record real-restart acceptance separately.
02

Conversation behavior

The user requests work through a normal conversation. Messages and tool results describe that work. Installation state is maintained by the update subsystem, not by a separate conversation widget. Reopening the App restores the conversation without an update history panel, update recovery notice or temporary verification dialog.

03

Target architecture

Separate the controller from candidate runtime. The current worker prepares and hands off; the supervisor controls activation and restoration; the new worker runs fixed system checks and a new acceptance Agent.

OpenProgram in-conversation self-update architecture Owner conversation directs an Agent to produce a candidate commit in an isolated worktree. A launchd-managed supervisor builds and probes the complete App, waits for turn-end handoff, then stops the old instance and installs the candidate. The new worker performs system verification and creates an acceptance Agent. Success commits installation; failure restores the old App through the supervisor. Owner chatGoal + update authorizationCurrent session / agent Isolated worktreeEdit · test · reviewimmutable candidate SHA Update recordsession · turn · criteriaatomic state + lock launchd supervisorone-shot · outside workertrusted current controller Build + cold probecomplete App bundlepackaged smoke · doctor Turn-end handofffinal response durableruntime quiescent Activate transactionstop App / workerretain previous.app New workerexpected identitystartup recovery System verificationrevision · health · doctorsingle dispatch claim Verification Agentsame agent profilenew self_update_verify turn commit install rollback + report passfail / timeout
1 · Prepare
Agent edits, tests, reviews, and creates a candidate commit in an isolated worktree.
2 · Handoff
Owner approves; the update record stores session, turn, candidate, and acceptance criteria; launchd manages the supervisor.
3 · Build
Build the complete App and run packaged smoke and cold probes while the old worker remains running.
4 · Activate
After the current response is durable and other tasks are paused or finished, stop App/worker, preserve previous.app, and activate the candidate.
5 · Accept
The new worker checks revision/doctor, then creates a new self_update_verify Agent turn to check the original objective.
6 · Terminal state
Delete previous.app after success; restore the old App on failure/timeout and write evidence back to the original session.

Candidate builds may execute candidate code, but sandboxing must deny writes to default state, installation, and update-record directories. Only a supervisor generated by the currently installed version may activate or restore.

04

Workflow and model responsibilities

Preparation reuses the normal conversational Agent’s planning, implementation, and review process. After submission, self_update/ durable control logic transitions states, without a generic new Workflow engine. Acceptance, diagnosis, and repair use frozen stage prompts and structured outputs; model outputs cannot directly stop processes, install, roll back, or change transaction state.

Stage and trigger
LLM work
Deterministic system action
Persistent output
PLAN
Owner states the objective
Clarify the objective; list scope, risks, and observable acceptance criteria.
Record the session contract; prepare freezes checkout, base SHA, authority, budget, and allowed paths.
Session record; after submission,
request.json goal/assertions
IMPLEMENT
Plan contract accepted
Edit source and tests in an isolated worktree; explain unaddressed items.
Restrict files, commands, network, and credentials; record diffs, commands, and exit codes.
worktree
change manifest
REVIEW
Implementation complete
Check correctness, security, rollback compatibility, schema, coverage, and unrelated changes.
Run agreed checks and save evidence; prepare rechecks clean Git and HEAD without treating Git checks as review proof.
Session/review records
candidate SHA
AUTHORIZE
Candidate buildable
Explain changes, verification, interruption, and recovery to the user without replacing their authorization.
Require owner approval, freeze candidate/scope/iteration envelope and stage configuration, then start the supervisor.
request.json
Stage configurations and iteration authorization
STAGE / ACTIVATE
Authorization and gates pass
No LLM call.
Complete App build, cold probe, maintenance, turn-end handoff, transactional replacement, and restart.
artifact manifest
transaction journal
VERIFY
New worker system gates pass
Run frozen check IDs; provide pass/fail/inconclusive and evidence per assertion. UI plans require the actual default App.
Startup recovery claims atomically; JobRunner creates a constrained Job with deterministic ID; supervise timeout and lease.
verifier-result-{attempt}.json
Signed receipt containing structured results
DIAGNOSE
Goal acceptance fails
Distinguish implementation, test, environment, and objective issues; propose the smallest next repair.
Restore the old App first; check iteration budget, authorization scope, and stop conditions.
diagnosis-result-{attempt}.json
next-attempt plan
FINALIZE
Success or stop
Report actual source, artifact, live revision, acceptance, and rollback states.
Commit installation on success; preserve evidence and candidate on failure; update the original session.
terminal state
session result
Restart dispatch

No ordinary Scheduler task

Scheduler time triggers and user-task authority do not suit the update protocol. Once JobRunner, session store, and owner-auth are available, the new worker executes recover_pending_updates().

Exactly once

Stable Job ID + atomic claim

The Job ID uses self-update:{update_id}:verify:{attempt}. The claim stores owner, lease, attempt, and job_id. Repeated startup does not resubmit an existing Job or terminal state.

Prompt contract

Stage context and tool isolation

The implementation Agent cannot bypass installation approval; the acceptance Agent has no source-write tools; diagnostic and repair Agents are read-only and propose structured output.verifier_config.py, diagnosis.py, source_repair.py stores stage configurations. New plans share goal, assertions, verification plan, iteration policy, and deadlines; inherited context never expands tool permissions.

Startup order

  • Load and migrate self-update state schema; fail closed on corruption.
  • Complete Job orphan reconciliation.
  • Start JobRunner and confirm admission/resource governance availability.
  • Complete revision, owner-auth, Web/WS, and doctor system gates.
  • CAS-claim the attempt, submit one verification Job, then start ordinary Scheduler.

Ordinary Scheduler startup failure does not block acceptance; JobRunner or system-gate failure requests supervisor rollback.

Controlled iteration policy

  • Default approve_each_activation: roll back and diagnose after failure; reauthorize before activating a new candidate.
  • Optional bounded_auto: owner explicitly authorizes at most 3 attempts, a total deadline, allowed paths, and a test set.
  • Pause for reauthorization on new dependencies, permission/approval changes, installer/supervisor changes, expanded paths, or changed objective.
  • Every attempt gets a new SHA, attempt, and Job ID; never overwrite earlier evidence.
  • Stop on repeated identical failures, rollback failure, environment-related inconclusive results, or budget exhaustion.
04a

Post-failure execution flow

Service restoration, diagnosis, source repair, and activation approval are separate stages. Diagnosis does not authorize installation.

Stage
Execution and persistence
Limits and subsequent actions
Automatic diagnosis after restoration
At owner authorization, new requests also freeze diagnostic model, profile, read-only tools, and output format. After a trusted controller confirms ROLLED_BACK and old-version system checks, it publishes this attempt’s diagnostic request before leaving maintenance. The worker creates a new task with stable Job ID self-update:{update_id}:diagnose:{attempt}.
Reuse JobRunner without Scheduler tasks, continuing the original turn, or moving the original session head. Allow only read, glob, grep, and list; prohibit source writes, shell, MCP, installation, and another update.
Fixed deadline and recovery
Bind diagnosis to original update, candidate, attempt, failure evidence, and rollback system evidence. Deadline is at most five minutes after rollback, or an earlier bounded_auto total deadline. Repeated startup reuses the request and Job ID; terminal or orphan Jobs are not rerun.
Bounded worker-side waiting collects results and cancels timeouts independently of Scheduler. A superseding update, owner cancellation, missing model, expiry, or evidence change stops diagnosis without changing ROLLED_BACK or re-restricting restored service.
Structured diagnosis
Record classification—implementation, test, environment, unclear objective, or insufficient evidence—reasons, original evidence references, and repair suggestions. Attach results to the original session as an independent Job and save an attempt diagnostic record.
Model classifications and suggestions require verification; they are neither test evidence nor new owner authorization. Environment, unclear-objective, and insufficient-evidence issues must not trigger automatic edits or activation.
Generate and apply source repairs
Create a separate stable-ID repair Job only when the new request freezes repair configuration, diagnosis is implementation or test, the old version is restored, and budget remains. The model uses the original model/profile and read-only tools to return structured edits. The controller checks original text and applies edits in a newly isolated worktree; the model executes no shell or Git.
Default scope is original changed_paths; bounded_auto uses original allowed_paths. Protected permission, approval, update-controller, installation-script, dependency, and Git-configuration files are never automatically edited. Stop on out-of-scope paths, symlinks, duplicate replacements, or source mismatch; do not modify the user’s original worktree.
New candidate and test evidence
The controller creates a new local commit and records the complete diff from original base, then runs frozen required_tests in a networkless sandbox without App write access. Record command, candidate SHA, exit code, and bounded logs. Recheck HEAD, Git binding, and tracked files after tests; tests that modify source invalidate evidence.
Repair Job and tests share a ten-minute post-rollback deadline, shortened by the original total deadline. A new update, owner cancellation, or expiry terminates the stage and test process group. Preserve evidence and isolated worktrees for terminal, failed, or partial edits; restart does not repeat edits/commits/tests or extend deadlines.
Reauthorization and activation
Candidate records bind original request, diagnosis, repair Job, full diff, and test results. The controller rechecks attempts, deadline, paths, dependencies, and repeated failures. approve_each_activation awaits owner approval for the new SHA; bounded_auto submits only within the original envelope.
A candidate record is not installation authority. Missing/failed required tests prohibit activation; environment, objective, or inconclusive diagnoses do not trigger automatic repair. The next candidate still requires full build, system gates, a new acceptance Job, and rollback on failure. Completed repair/testing does not establish implemented subsequent activation.

Old updates without frozen diagnosis retain their original recovery behavior; never synthesize configuration from mutable profiles. Diagnostic requests, claims, and results live in the original update directory, with one pending pointer at self-updates root. A new update revokes the old pointer under the same storage lock. Execution rechecks durable Job, current worker claim, frozen inputs, and unexpired deadline, rejecting forged source or input drift while restoring an old queue. Owners stop diagnosis through existing Job cancellation, not activation-preceding self_update_cancel. Diagnosis must not overwrite verifier verdict, signed acceptance results, or rollback conclusion.

Repair likewise requires independently frozen configuration in the original request; old requests gain no source-write capability. Default initial authorization includes isolated-worktree repair and listed tests after rollback, but not reinstalling. Only new requests explicitly selecting bounded_auto and approving the complete iteration authorization below allow scoped reinstallation. Repair output allows at most 32 text edits totaling 1 MiB. Each old_text must uniquely match a regular UTF-8 file; null old_text creates only nonexistent files. null new_text permits deletion only when old_text equals the complete file. The repair Job uses wait=true, creates no ordinary follow-up Agent, and leaves the original head unchanged. After the model Job completes, the owner calls self_update_repair_cancel(update_id) in the original session to cancel still-running candidate tests. Record candidate_ready only after the new SHA, complete diff, all configured tests, and post-test checks pass; no required_tests means awaiting_tests. Missing/failed configured tests or failed post-test checks mean failed; explicit cancellation and expiry mean cancelled and expired. None means installed.

04b

Authorization and updates for repaired versions

Every update retains independent requests and evidence; an iteration sequence shares the original goal, scope, attempt budget, and total deadline.

Contract
Controller behavior
Rejection and recovery
Original authorization and compatibility
The first request freezes root update, owner, session, goal, assertions, original base, model/profile, mode, paths, tests, max_attempts, and optional total deadline. bounded_auto requires a complete total deadline, paths, and nonempty tests. Approval explicitly includes bounded reinstallation; the immutable request stores an authorization-record digest.
Old requests without this record gain no reinstallation authority even if policy.mode=bounded_auto. Current profiles, model suggestions, and candidate_ready cannot manufacture authorization. Changed objectives, wider paths, or added dependencies require a new owner request, not mutation of the sequence.
Revalidate evidence
The candidate must come from this attempt’s completed repair Job. Request, diagnosis, Job output, generated intent, candidate manifest, and test-log digests agree. Reread real Git: the dedicated worktree still belongs to the original session, HEAD equals the approval SHA, the tree is clean, the SHA newly descends from the prior candidate, and full diff from original base satisfies scope/protected-path checks. Check before approval display, after approval, and before child reservation; supervisor build/activation retain drift checks.
Require nonempty required_tests with matching command, SHA, exit code, and logs for every item; model claims or manifest success fields alone are insufficient. Missing, drifted, failed, or out-of-scope evidence stops submission with a reason; reapproval cannot bypass it. awaiting_tests candidates without configured tests require a new owner request specifying tests.
Two approval modes
By default, self_update_retry(update_id, candidate_sha) in the original session forces one-time approval for the exact SHA, displaying full changes, tests, and remaining budget. It cannot become a persistent allow rule. bounded_auto submits automatically from original authorization and real evidence, without another model tool call.
Owner rejection or approval timeout creates no successor. Both modes verify current owner, original goal/assertions, successful rollback, remaining attempts, and total deadline. Only implementation/test failures may continue; environment, unclear objective, inconclusive, unverified rollback, or consecutive identical failures stop. Failure fingerprints use trusted categories and failed assertion/check IDs, not model explanations, timestamps, SHAs, or random IDs.
Attempt counts and immutable successors
The first update is attempt=1. Successors use new update_id, SHA, attempt, and stage Job IDs, recording root/parent relations. Original goal, assertions, base, owner, model/profile, and policy stay fixed; only current identity/output schema are rebound. At most 3 attempts includes the first.
Reserve one unique successor and next attempt under the same storage lock. Reservation consumes budget permanently across submission failure, cancellation, and restart. A parent cannot have a second successor. Each build/acceptance retains per-attempt timeouts within the original total deadline. After expiry, only necessary safe rollback/recovery remains allowed; no further repairs or installation.
Interruption, concurrency, and complete updates
Persist the next-stage request after candidate results and before removing the repair pending pointer. Startup resumes approval/submission from that request. Save fixed child ID and budget reservation first, publish child request and active ownership, then invoke the existing external supervisor. Repeated recovery can only finish this submission, never re-edit, retest, or expand authority.
Other active updates or maintenance block submission; a new owner update revokes pending work from the old sequence. Default approval requests release after the approving turn is durable; automatic submission uses persisted authorization, results, and submission records rather than a fabricated chat turn. Successors still perform full packaging, cold probe, system gates, new verifier, and commit/rollback. Submission failure retains inspectable records without skipping stages.
Cancellation and visible state
self_update_iteration_cancel(update_id) persists a stop marker in the original owner session, stopping the sequence’s unsubmitted approvals/automatic submissions and cancelling running diagnosis/repair Jobs and candidate tests. Created but unactivated children use existing cancellation. Other sequences and ordinary tasks remain unaffected. State shows root, parent, current child, attempt/max_attempts, total deadline, and stop reason.
Transactions already activating/verifying cannot be forcibly interrupted by deleting requests. Prohibit later attempts while existing transaction logic finishes verification or safe rollback. Submission phases distinguish awaiting_approval, submitting, submitted, and stopped. submitted means handed to the supervisor, not installed successfully; success still requires child SUCCEEDED and acceptance evidence.

This stage reuses SelfUpdateStore process locking, JobRunner, mandatory approval, trusted supervisor, and transactional installer, without another Scheduler or generic workflow executor.self_update/iteration.py remains a side-effect-free policy decision; the new submission coordinator validates evidence, reserves budget, persists state, and recovers. Source responsibility boundaries do not replace real restart, rollback, and multi-attempt acceptance.

05

Design decisions

V1 adds only boundaries required for the goal, without another Job system, plugin system, or update dependency.

Controller

Update control must be outside the worker

Use macOS launchctl submit to launch a one-shot supervisor outside the worker process tree, allowing activation, verification timeout, and restoration after worker/App shutdown.

Candidate

Deploy immutable commits only

Candidates must come from dedicated clean worktrees with explicit bases. Reread HEAD during build; discard artifacts on drift rather than deploying directory snapshots.

Artifact

V1 always builds the complete App

Reuse complete runtime, Electron builder, and packaged smoke. Do not yet choose wheel, asar, or full-App paths by changed files, avoiding multiple completion semantics.

Handoff

Tool calls do not stop the worker directly

self_update_prepare only persists the request and starts the supervisor. After the assistant final, session commit, and change attribution are durable, dispatcher writes turn_released.

Continuation

Startup recovery creates a new acceptance Job

The new worker atomically claims the update and reuses JobRunner to create a background task with source=self_update_verify. Ordinary Scheduler neither creates, polls, nor retries it.

Rollback

Goal failure also restores the old App

Retain previous.app until system checks and goal acceptance both pass. System errors, Agent fail/inconclusive verdicts, and timeouts restore the old App while preserving candidate worktree and evidence.

06

State machine and persistence

One directory per update id. Every transition uses temporary files, fsync, and replace; active.json stores only the current update id.

PREPARINGRequest, authorization, and candidate frozen
STAGINGComplete build and isolated probe
READYWait for turn release and idle
ACTIVATINGStop and replace, retaining previous
VERIFYINGNew worker + acceptance Agent
SUCCEEDEDCommit installation, clean previous
Pre-activation terminal

ABORTED

Authorization rejection, build failure, candidate drift, cancellation, or idle-wait timeout; the old App was never replaced.

Recovered terminal

ROLLED_BACK

Candidate activated but system/goal acceptance failed; old App restored and old revision reverified.

Operator terminal

NEEDS_MANUAL_RECOVERY

Unable to determine old/new App state automatically, or rollback failed; stop automatic actions and retain the entire transaction.

~/.openprogram/self-updates/
  active.json                 # schema + update_id
  maintenance.json            # admission ownership
  <update_id>/
    request.json              # immutable intent
    state.json                # current phase + owner
    events.jsonl              # append-only evidence
    verifier-config.json      # frozen model / tools / optional plan
    diagnosis-config.json
    source-repair-config.json
    iteration-root.json       # frozen root authority
    iteration-next.json       # bound proposal + unique child reservation
    iteration-status.json     # submission / stop evidence
    supervisor.sh             # trusted copied controller
    supervisor.log
    artifact.json             # path + hashes
    verifier-result-{attempt}.json
    diagnosis-result-{attempt}.json
    source-repair-result-{attempt}.json
    controller/               # trusted runtime and installer
    bootstrap.json
    recover.sh
/Applications/.openprogram-app-install.*/
  previous.app                # transaction path bound in state.detail

request.json stable fields

  • schema, update_id, created_at
  • session_id, origin_turn_id, origin_assistant_id, agent_id
  • repo, worktree_id, base_sha, candidate_sha
  • changed_paths, pre_update_evidence
  • goal, assertions, timeout_seconds; acceptance goals reside directly in request, without separate acceptance.json
  • iteration_policy: mode, max_attempts, deadline, allowed_paths, required_tests
  • pre_update_evidence binds the iteration-record digest containing root/parent update, original authorization digest, sequence attempt, and unique child ID. Every successor has its own directory; original requests and historical terminal states are immutable.
  • app_path is fixed to normalized /Applications/OpenProgram.app

state.json mutable fields

  • phase, revision, updated_at, detail
  • attempt, dispatch.job_id, dispatch.claimed_by, dispatch.lease_until
  • last_event: deterministically repair the final event when state was persisted but event append was interrupted

Accept no URL, arbitrary installer path, shell command, state-directory override, or port override. Candidates cannot choose the controller.

07

Complete sequence

Build takes most of the time without affecting the old instance; only activation/restart causes temporary unavailability.

1 · Agent prepares candidateEdit in a separate worktree; run affected tests, full gates, specification review, and quality review; commit and confirm a clean tree.owner worker
2 · Create requestself_update_prepare recomputes base/candidate/changed paths from Git, saves acceptance, and shows exact authorization. Reject the current main checkout, dirty candidates, version mismatch, and a second active update.owner worker
3 · Start supervisorThe current trusted runtime generates a fixed supervisor file with mode 0700 and uses launchctl submit for a one-shot job. Pass only update id and state directory.launchd
4 · Build and probeSupervisor calls the complete App build in the candidate worktree. Current trusted smoke/probe contracts validate manifest, assets, startup, doctor, /chat, and expected revision. Sandbox the candidate build.supervisor
5 · turn-end releaseAgent reports “candidate committed, update scheduled.” After final node, session status, turn commit, and change attribution are all durable, dispatcher sets turn_released=true.dispatcher
6 · Wait for idleReject new write turns and wait for other foreground turns and running Jobs to finish naturally, up to ten minutes by default. Timeout aborts before worker shutdown without cancelling other tasks.maintenance gate
7 · ActivateRecord original App/worker/launchd state; stop App and worker; move old App to transaction/previous.app; move staged App to the canonical path; re-register Launch Services; restore original startup state.supervisor
8 · System verificationThe new worker must prove owner identity and candidate revision and pass doctor, Web, and WS gates. On failure/timeout, supervisor stops it and restores previous.app.supervisor
9 · Automatically trigger AgentAfter JobRunner is ready, startup recovery atomically claims the attempt and creates a stable-ID constrained Job with source=self_update_verify. Ordinary Scheduler is uninvolved.new worker
10 · Commit, restore, or iteratePass commits installation; any other verdict/timeout first restores the old App. Default waits for new authorization; bounded_auto creates the next attempt within the original scope and remaining budget. All results return to the original session.supervisor
08

Two-layer acceptance contract

Deterministic system checks first establish identity, startup, and service availability; the Agent then assesses the requested behavior. Neither layer replaces the other.

Layer
Executor
Required checks
Failure result
Pre-update evidence
Modification Agent + review
RED/GREEN, affected/full tests, docs/checklinks, clean candidate, and two independent review verdicts.
Do not create an update request.
Artifact gate
supervisor
Complete runtime manifest, Electron bundle, version match, packaged smoke, and isolated cold start.
The old instance keeps running.
Live system gate
supervisor
Default 18100, owner-auth identity, expected candidate revision, doctor, and basic Web/WS connectivity.
Automatically restore the old App.
Goal acceptance
verification Agent
Read every original assertion and check real public entry points. UI changes require operating the canonical App; API/CLI changes require invoking real entries.
fail/inconclusive verdict triggers automatic restoration.
Evidence binding
controller
Each assertion stores command/page/node, observation, artifact revision, and time; verdict covers all required assertions.
Missing fields mean inconclusive.
Verifier authority

Read and operate acceptance entry points

Tool allowlist is read/glob/grep/list plus self_update_observe. Check IDs execute only frozen local API, CLI/test, or UI actions. No generic Browser/Computer Use, shell, source edits, commits, deployments, external messages, or second-update authority.

Semantic limit

Agent verdict assesses evidence

Candidate runtime cannot independently prove its own trustworthiness. Supervisor decides system gates; the Agent gives structured pass/fail/inconclusive for user goals with inspectable evidence.

Timeout

No unlimited waiting

System startup: 90 seconds; Agent acceptance: 10 minutes by default; entire update: 30 minutes by default. Timeout never means success.

08A

Real UI, API, and test evidence

Acceptance Agent uses the same JobRunner and frozen configuration. Added capabilities are bounded acceptance operations, not another Agent, Scheduler, or generic browser-control service.

Acceptance entry
Execution contract
Irreplaceable evidence
Default App UI
A constrained adapter connects to the actual main window of /Applications/OpenProgram.app. Bind App process, window, owner, update_id, attempt, and current worker identity, rechecking before and after actions. The model cannot supply CDP addresses, arbitrary targets, JS, or file paths.
Save real screenshots, accessibility trees, current route, window identity, and time. Receiving only /chat HTML, DOM text, or an embedded Web-tab screenshot cannot prove main-App visual or interaction requirements.
UI interaction
Freeze original-session main-window checks: screenshot/accessibility tree, chat scrolling, and session/dag switching. New packages declare UI protocol 1/2/3 by capability; legacy protocol 4 remains read/rollback-compatible only, rejecting temporary test-object operations.
Success must bind original session, worker, Job, and target window and restore owned scroll/view changes; user-input interruption must preserve user choices. Arbitrary clicks, messages, settings, external navigation, file transfer, clipboard, and code execution are unauthorized.
Local API
Keep owner-auth and loopback restrictions; use frozen method, path, arguments, and response bounds. Prohibit redirects, model-supplied authentication, and arbitrary URLs. Check the same worker/candidate identity before and after calls.
Real responses must support the assertion. A health check does not prove functional correctness; HTTP 200, page titles, or model claims alone cannot establish a pass.
Constrained tests and CLI
Fixed CLI version/help calls use the installed App runtime; frozen test:python runs in the candidate worktree. Accept no model shell. Native acceptance sandbox is single-process and denies fork, posix_spawn, and subprocesses; this does not change separate build/repair-test execution policy.
Source tests cannot replace post-install acceptance, and CLI help cannot prove arbitrary features. Use a minimal networkless environment denying writes to default data and App. Timeout, cancellation, output overflow, identity changes, or cleanup failure cannot pass. Do not start a second profile/App.
Frozen verification plan

Capabilities determined before updating

Each assertion binds evidence kind, check ID, allowed targets/actions, and budget. Bind the plan digest into request evidence and verifier configuration. Prepare checks dependencies and permissions; old configurations do not automatically gain new UI/test permissions.

Trusted evidence receipt

Execution adapters save evidence

Reuse the controller’s constrained observations and signed receipts, adding evidence kinds without accepting model-signed results. Bind each item to update, attempt, SHA, worker, check ID, time, and content digest. Bound screenshots/output; exclude authentication from logs, screenshot exports, and model context.

Failure and cleanup

Missing evidence cannot pass

Record precise reasons for closed windows, identity changes, denied permissions, vanished objects, judgment-affecting truncation, provider failure, and timeout. Adapters release their own connections/test resources without closing user windows; cleanup failure is recorded and blocks pass. Reconnects/restarts never reset the total deadline.

Page content, test output, and candidate code are untrusted data and cannot expand tool authority. Allowed-action enforcement must cover UI, IPC, and backend write entries; DOM selectors, button text, or read-only prompts alone cannot prevent side effects. Automatic acceptance avoids other sessions and personal browser data. User activity in the target window stops automatic interaction while preserving evidence; waiting cannot exceed the original deadline.

09

Failure semantics and recovery

Failure state must state whether the candidate activated, which revision is running, whether the old App remains, and whether acceptance executed.

Failure point
System action
User-visible result
Authorization denied, dirty candidate, or no commit
Do not start supervisor or change worker/App.
Original session shows a stable error and repair conditions.
build, tests, manifest, packaged smoke, cold probe
Stop staging; old instance keeps running; clean unverified artifacts.
The same session receives the failed step and log summary without restart.
Other tasks do not finish within the wait deadline
Exit maintenance and abort the update without cancelling other turns/Jobs.
Show quiescence-timeout and the still-running task list.
Old App/worker cannot stop
Leave installation unchanged and restore prior maintenance state.
Show exact PID/launchd state without claiming activation.
App move, Launch Services, or new-worker startup fails
Move new App to failed.app, restore previous.app and original startup state.
Reopen the old App; result card shows ROLLED_BACK.
Revision/doctor/Web/WS gate fails
Create no acceptance Agent; restore previous.app immediately.
Original session receives system-gate evidence.
Acceptance Agent fails, is inconclusive, or times out
Save verifier-result and restore the old App first. New requests with frozen configuration diagnose automatically. Allowed implementation/test failures proceed to repair/testing; default waits for authorization only before reinstalling, while complete bounded_auto authorization permits scoped automatic submission. Inconclusive, environment, and objective issues stop reinstallation.
Show each assertion, restored revision, attempt, and remaining budget.
JobRunner, provider, or model unavailable
Do not defer through Scheduler; mark environment-related inconclusive and restore the old App.
Show missing dependencies and retry conditions without mislabeling a code defect.
Rollback itself fails
Stop all automatic iteration, retain previous/candidate/transaction, and enter NEEDS_MANUAL_RECOVERY.
Show actual App, revision, processes, and read-only recovery instructions without claiming restoration.
Supervisor crashes or machine restarts
Before the next worker/App startup, recover idempotently from phase, canonical App, previous.app, and revision.
Add no RECOVERED phase. Preserve SUCCEEDED, ROLLED_BACK, or ABORTED according to actual recovery outcome; uncertainty becomes NEEDS_MANUAL_RECOVERY. Store recovery events/evidence separately.

App-independent recovery entry

Before candidate activation, the trusted current version saves controller/runtime/installer and generates update-specific ~/.openprogram/self-updates/<update_id>/recover.sh, with a matching one-shot login task in the local user’s ~/Library/LaunchAgents/. Script, state directory, and runtime all reside outside the App, independent of PATH OpenProgram, the damaged App, ordinary worker, and Scheduler. Without a complete verifiable saved copy, do not generate replacement control code or guess recovery.

Entry
Behavior
Constraint
recover.sh status
No arguments still means status only
Saved runtime reads this update, maintenance ownership, and manual-recovery results, returning the fixed recovery-entry path.
No model, App mutation, or new update; reject other arguments, arbitrary commands, paths, and profiles.
recover.sh repair
Reuse the manual-confirmation flow below. If App/CLI cannot start, the owner can still inspect the plan and authorize recovery in a terminal.
Interactive confirmation and the original ten-minute deadline remain required; acceptance, journal, identity, and permission constraints remain unchanged.
At next user login
or recover.sh resume
Revalidate saved bundle, request, and fixed script; invoke the original supervisor to continue existing handoff/recovery by persisted phase, without synthesizing turn release.
Reuse original update, rollback, commit, and repair deadlines/results. Manual state without manual authorization stays in maintenance; approve no new candidate and create no second acceptance Job.

Login filename is ai.openprogram.self-update.recovery.<update_id>.plist, executed at most once per user login and removed when complete: RunAtLoad=true, without KeepAlive, schedules, or persistent daemons. Writing the plist does not immediately load another controller; normal updates retain the original one-shot supervisor and controller lock. If both App and controller are unavailable in the current login, the terminal script is the explicit entry. After reboot, automatic recovery requires this user to log in; no claim is made for logged-out or locked-disk execution.

Publish binding records for recovery entry, fixed arguments, and plist before supervisor submission and candidate activation. Operate only canonical user-owned directories and exact files for this update. Reject conflicting existing content, symlinks, nonregular files, or unsafe permissions without overwriting. Repeated preparation validates saved copies rather than copying from the replaced App. Startup failures retain independent diagnostics and never clear maintenance or extend authorization.

After the original update terminates and releases maintenance, remove only this login plist if its content still matches binding records. Retain saved runtime, scripts, original evidence, and recovery results for inspection. Cleanup failure records diagnosis without changing verified outcomes; rerunning a leftover one-shot task must not recover a completed update. A damaged entry, permission drift, or insufficient evidence still requires manual handling; no arbitrary file repair or forced maintenance clearing is provided.

Task pause, restart, and continuation

On entering maintenance, the worker uses existing canonical pause commands to reach durable execution checkpoints. Each update saves execution id, pause command id, and observed version, resuming only tasks it paused. Previously paused, awaiting-input, cancelled, or completed tasks do not resume automatically. Queued Jobs that never ran retain queue identity and do not participate in active-execution waiting.

Supervisor replaces the App only after canonical active attempts and legacy running tasks finish. After commit or verified rollback and maintenance release, the new worker continues tasks through original continue commands and the Job resource queue. Fixed command ids prevent duplicate execution on repeated recovery. Failed pauses or unpausable tasks that miss the deadline abort the update and resume already paused work.

Checkpoint recovery retains execution-contract validation. Changed tool implementations or permissions prohibit replaying old operations and preserve the old checkpoint. Only a continue initiated by this update and rejected for contract change may create a fixed-identity replanning Job from the original task/history, retaining original tools/permissions and rechecking execution/update ownership before running. Unresolved side effects, user cancellation, or other session activity prevent automatic replanning. Task-recovery failure does not change installation acceptance. The initiating session gets a durable terminal follow-up reading original goal/history/update result; it neither replaces the verifier nor gains interactive reactivation authority. Old records do not retroactively create follow-ups.

Acceptance entries are supervisor waiting, worker reconciliation, and real canonical pause/continue. Cover queued tasks, checkpoint recovery, duplicate worker recovery, user pause/cancel, unreleased maintenance, failed updates, and exactly one original-session follow-up. Status: implementation and focused tests complete; independent reviews, full tests, and real-App acceptance pending.

Explicit manual recovery

openprogram self-update status [update_id] --json reads maintenance ownership, original terminal state, and recovery results without models, new updates, or App mutation.openprogram self-update repair <update_id> is an independent local-owner control entry, separate from chat/Scheduler. An interactive terminal must confirm displayed update id, target revision, recovery action, and plan digest; no skip-confirmation flag. Recompare original state, maintenance ownership, installation journal, and trusted controller at confirmation; reject stale confirmation after any change.

Original transaction evidence
Authorized action
Pass condition
Not irreversibly committed; previous identity and old revision known
Original trusted installer idempotently restores the old App without activating a new candidate.
Canonical old-App identity matches; restarted old revision, owner, doctor, Web, and WS all pass.
Already committing/committed with a complete original accepted signed decision
Only finish the original irreversible commit; confirmation cannot manufacture goal pass or authorize initial commit from activated state.
Original grant/result/decision/journal identities agree; canonical candidate and restarted live version pass new checks.
ABORTED and still prepared
Only verify the unchanged old App and restore execution; no installation/rollback.
Prepared journal unchanged, running version is not candidate, all system checks pass.
Unknown identity, missing evidence, damaged trusted controller, or failed check
Retain maintenance and all recovery files with a precise failure. Do not guess paths, delete the old App, or continue automatic iteration.
Show manual work still required; do not mark the original goal achieved.

Each confirmation creates an independent durable recovery request bound to original update/transaction identity with a ten-minute deadline, never extended by retries/restarts. Reuse saved controller/runtime/installer only, launched under a one-shot launchd label including recovery request id; do not recopy control code from the damaged App. The recovery process uses the original installation transaction to validate canonical App, restarts the default worker, and verifies the new process so startup checks and ordinary Scheduler resume normally. Interruptions allow idempotent recovery/worker restart within the same deadline, but no verifier Job or second update.

Original terminal state, verdict, and deadline remain unchanged. Manual recovery writes a separate resolution receipt distinguishing old-version restoration, accepted-version restoration, and unactivated-update restoration. Atomically release this maintenance owner only when the receipt matches new system evidence. Failure, expiry, or no confirmation keeps ordinary tasks prohibited; another attempt needs fresh confirmation. This is not arbitrary shell, file repair, or forced maintenance clearing.

10

Permissions and trust boundaries

Permission to edit code and permission to replace the running product are separate. The latter grants one candidate SHA or an explicit bounded multi-attempt envelope.

Offline build inputs

The controller obtains pinned uv, managed Python, and dependencies from the saved trusted complete runtime. Before reuse, byte-compare candidate uv.lock and product-runtime.json; mismatches or missing inputs fail before activation, without silently reusing incompatible dependencies or allowing candidate network access. Dependency changes require matching complete-runtime inputs first; candidates cannot choose download sources or inherit owner credentials.

Copy runtime and existing npm/uv/Electron-builder/node-gyp caches into a private build-home; macOS uses independent copy-on-write copies, never shared writable caches. The trusted controller selects Electron platform ZIPs by fixed version, architecture, and release SHA-256, copies them into candidate-read-only update inputs, and uses them through electronDist. Cache hits never skip integrity checks. Candidates still rebuild Web, docs, wheel, and Desktop archive, installing only the new wheel into private runtime. Normal official-Release builds remain unchanged. Copy, build, and checks share the original deadline; cache hits are not acceptance evidence.

Build processes cannot access external networks, default App, or user state. Packaged worker smoke may bind/access only one controller-selected private loopback port, explicitly excluding 18100; sandboxing still denies all other networking. Fixed checks requiring host graphics do not widen candidate sandbox permissions: the controller invokes system sips with fixed arguments for four fixed icon sources, then runs a Playwright Chromium probe using fixed trusted-runtime Python. Before/after execution, candidate browser-resource tree hashes must match saved trusted resources. Worker, icon, and browser checks must complete and enter manifest/receipt before activation; no passing by exposing the default worker or skipping checks. Actual packaging/installed-App acceptance appears at the end.

Trusted controller

Control logic comes from the currently installed version

Before activation, copy supervisor/installer from current runtime and pin hashes. Candidate supplies no supervisor, App path, state path, command, or recovery policy.

  • One-shot launchd label includes update id
  • 0700 controller, 0600 state
  • Fixed default profile, 18100, canonical App path
Candidate code

Build execution with restricted permissions

Candidate build/smoke executes source, so run sandboxed with minimal environment. Deny provider credentials, default session state, installation directory, and update records.

  • Write only candidate worktree and staging temp
  • Do not inherit tokens/API keys
  • Network off by default; dependencies from lockfile/cache
User authority

Activation must remain within owner approval

self_update_prepare joins mandatory approvals that bypass cannot skip. Default binds the candidate; bounded iteration binds goal, allowed paths, maximum attempts, deadline, and prohibited changes.

  • cron/scheduler/sub-agent/MCP cannot initiate
  • Approval never becomes a generic persistent allow rule
  • Scope, dependency, or permission changes invalidate it
Verification

Acceptance Agent permissions independently narrowed

Automatic acceptance cannot edit source or update again. It may only read evidence, run constrained checks, and operate local UI; extra side effects require inconclusive.

  • Fixed agent/profile/model snapshot
  • Evidence required for every assertion
  • Submit verdict with supervisor one-time token
11

Public entries and internal boundaries

Users still only need chat. Tool arguments remain structured; models receive no installation commands.

Mutating tool · forced approval

self_update_prepare

{worktree_id, candidate_sha, goal, assertions[], iteration_policy?, verification_plan?}. After mandatory approval, validate candidate, freeze configuration, write request, and start supervisor. Return update_id, phase, candidate_sha, base_sha, and changed_paths, not installation success.

Read-only tool

self_update_status

{update_id?}. Return phase, steps, current/candidate revision, active App, rollback availability, and verifier verdict.

Pre-activation control

self_update_cancel

Effective only in PREPARING/STAGING/READY. Reject cancellation after ACTIVATING; supervisor completes commit or rollback.

Repair control

self_update_repair_cancel

{update_id}. The original owner session cancels post-rollback source repair or candidate tests without rewriting acceptance/rollback conclusions.

Retry · forced one-shot approval

self_update_retry

{update_id, candidate_sha}. Approve a repaired SHA in the original session after real-evidence verification; recheck identity, candidate, and budget before/after approval. Return fixed child ID and submission state, never fabricated installation success.

Iteration control

self_update_iteration_cancel

{update_id}. Stop the entire iteration sequence and pending/running repair tasks. Activated transactions finish necessary verification/recovery but cannot create another attempt.

# Job result; controller wraps it in a signed receipt
{
  "schema": 1,
  "update_id": "su_...",
  "candidate_sha": "...",
  "attempt": 1,
  "verdict": "pass | fail | inconclusive",
  "assertions": [
    {
      "id": "acceptance-1",
      "status": "pass",
      "entry": "default App",
      "observation": "...",
      "evidence_refs": ["observation-id"],
      "observed_at": 1.0
    }
  ]
}

DAG representation

The initiating assistant node records self_update_id. Post-restart verification branch uses:

  • source=self_update_verify
  • caller=origin_assistant_id
  • predecessor=null, without changing user main-branch HEAD
  • Results return through existing Job completion/follow-up mechanisms

UI renders “Automatic post-update acceptance,” not a user message.

11A

Update records and normal conversation results

Installation, verification and rollback retain their durable update records. Conversations display their normal messages and tool results only. The message list does not mount a software-update history component, issue update-history polling requests, or expose update management forms. Desktop recovery retains its routing and acknowledgement protocol without adding update recovery notices to the conversation.

Projection field
Writer and provenance
Read meaning
Missing or stale
update_id, root/parent, origin session, attempt
Validated request and iteration relations.
Identify the update, original session, and parent/child attempts; identity is supplied only by the server.
Reject inconsistent ownership; renderer values cannot rebind it.
phase, state_revision, updated_at
SelfUpdateStore durable phase, increasing revision, and time.
Transaction progress; state_revision is a state version, not a Git SHA.
Corrupt/future schemas return explicit errors, not “no update.” Reads cannot initialize, repair, or advance transactions.
candidate_revision, changed paths
Prepare verifies Git and freezes the request.
Target source revision and changes, not installed/running status.
Never fill running revision from candidate SHA.
last_verified_runtime
Select and validate controller system_gate, committed_system_gate, restored_system_gate by phase; pre-activation old version comes from previous_system_gate. Manual recovery uses the corresponding validated recovery receipt.
Last verified SHA, worker PID, verification time, and evidence source. SUCCEEDED selects commit evidence, ROLLED_BACK restoration evidence, VERIFYING candidate evidence. Installation-journal committed is not another update phase.
Without matching evidence during transitions, ABORTED, or manual recovery, report unknown; do not choose the latest apparently valid old gate. This is not continuous liveness proof.
target_app and rollback state
Canonical App path from request, transaction manifest, and corresponding recovery state.
Distinguish installation target, recoverable copy, restored state, and manual recovery required.
Do not label target path “actual active App”; a previous saved copy does not imply rollback remains available.
Acceptance, diagnosis, repair, and next submission
Corresponding validated result/receipt and next-candidate summary.
Per-assertion verdict/evidence, repaired SHA, approval status, budget, and child ID. Verifier pass is not installation commit; update phase becomes SUCCEEDED only after commit completes.
No result yet means pending; no evidence means inconclusive. Continued repair after rollback cannot overwrite the original failure.

Shared read contract

openprogram/self_update/control/projection.py provides shared read-only projections for dedicated tools, Web, and Running. Owner-auth route is GET /api/self-updates?session_id=… and GET /api/self-updates/{update_id}?session_id=…, validating origin session. History uses bounded stable-cursor pagination independent of the active pointer. Evidence is revalidated through the same update’s /evidence?session_id=…&evidence_id=… route, returning only observations referenced by signed results.

Projection includes only allowlisted fields, bounded summaries, and opaque evidence IDs, excluding configuration, tokens, signing keys, environment variables, and arbitrary log paths. Evidence reads recheck owner/session/update/receipt bindings and never permit arbitrary files. Read state and associated results consistently; if no coherent snapshot is possible, return retryable error. Reads trigger no claim, repair, Job, or Scheduler.

Update projection APIs remain read-only and session-scoped for explicit tool reads. Chat does not poll these APIs. Stored update status does not infer success or failure from a disconnected window.

Conversation

Normal results

Tool results and durable result delivery use the existing conversation message flow. No separate update history, pagination, evidence viewer or update request form is mounted in chat.

Durable result delivery

Results do not depend on an online window

Controller persists terminal state first; worker delivers bounded results to the original session through existing Job completion/follow-up. Stable delivery keys use update, attempt, and result type; repeated startup or lost ACK creates no duplicate message. Deliver even if terminal state precedes verifier creation or the session is closed. Deleted sessions retain undelivered status, without redirecting or creating user tasks.

No false completion

Show execution and installation outcomes separately

Show acceptance-task completion, goal pass, installation commit, rollback completion, and later repair separately. Deterministic code generates notices without another LLM or triggering installation. DAG retains origin-caller/verification-Job relations without changing main-branch HEAD.

Desktop restart returns to the original session

Reuse apps/desktop/worker-start-url.js owner-auth startup and existing /s/{session_id} routes. Before App shutdown, supervisor saves a trusted reopen intent bound to update, attempt, origin session, owner, deadline, and startup action. Desktop receives only an opaque update ID; authenticated service validates the session and constructs a same-origin route. Candidate, model, CLI arguments, and renderer cannot choose arbitrary URLs, paths, or auth fragments.

Consume the intent only during this update-triggered reopen; preserve normal startup, current windows, center tabs, and stable Release updater. ACK only after authentication and original-session load. Crashes before ACK may retry navigation without reactivation or verifier recreation. Only the designated main window consumes it; repeated consumption navigates idempotently without duplicate tabs. After user navigation away, do not force a return or insert a special recovery prompt.

Owner changes, missing session, expiry, identity mismatch, or unavailable recovery APIs retain ordinary startup and internal reasons. Do not navigate elsewhere, create sessions, or claim installation success from navigation failure. If App was closed, follow saved startup state; prepare must show and authorize opening a window when visible-App acceptance requires it. Both update and rollback versions must support minimum read/recovery protocols; reject incompatibility before activation.

12

Reference implementations and adoption

References establish process, update, and worktree boundaries; this design still defines candidate sources, permissions, and acceptance.

Adopt

Git worktree

Use linked worktrees to isolate candidate HEAD/index/working directory; serving checkout and existing dirty user checkouts are not build inputs.

Adapt

Apple launchd agent

Use an independent process within the user login session. V1 uses one-shot launchctl submit, without a persistent updater daemon.

Adapt

Electron update lifecycle

Adopt staged artifacts, restart, and explicit state; candidates come from local commits rather than remote feeds.

Reject

Electron autoUpdater

macOS automatic updates require signing and release feeds, which do not cover unpublished local candidates, Agent acceptance, or current unsigned development-App contracts.

Reuse repository implementation

Stable release automatic updates

Reuse complete artifacts, manifests, runtime probes, and platform installation boundaries without changing stable Release discovery, download, or user installation.

Reuse repository implementation

Agent worktree

Reuse worktree ownership, commit, merge/discard, and approval semantics. Self-update reads candidates only and never automatically merges the user’s main branch.

13

Phased implementation plan

Review each task independently; start the next only after specification and quality reviews pass. Production filenames are target locations; first check existing shared primitives.

TASK A · DURABLE CONTRACT

State model and single-instance lock

Add openprogram/self_update/, using only stdlib JSON, fcntl, and atomic replace.

  • types.py: phase, request/result schema
  • store.py: create/load/transition/claim/event journal
  • RED: corrupt JSON, concurrent create, illegal transition, duplicate claim, future schema
  • Gate: unit + component state recovery
TASK B · STAGE AND TRANSACTION

Complete artifacts and deferred installation commit

Reuse Desktop build/install scripts without new updater dependencies.

  • package-and-install-app.sh: separate build-only output
  • install-app.sh: prepare/commit/rollback or equivalent defer-commit interface
  • upgrade.py: extract shared identity/doctor probe
  • RED: build failure does not stop the old worker; post-activation failure restores the old App
TASK C · CHAT HANDOFF

Dedicated tools, approval, and turn-end release

Tools submit durable intent without stopping execution.

  • programs/tools/system/self_update/: prepare/status/cancel
  • agent/permissions/approval.py: forced owner approval
  • agent/dispatcher/: release after final becomes durable
  • RED: reject dirty SHA, non-owner source, concurrent update, and non-durable final
TASK D · EXTERNAL SUPERVISOR

launchd one-shot controller

Current trusted version generates fixed controller; candidate supplies only commit content.

  • self_update/supervisor.py and fixed shell handoff
  • build sandbox, maintenance gate, quiescence timeout
  • Lifecycle of state/log/transaction directories
  • RED: supervisor completes after worker stops; repeated startup is idempotent
TASK E · RESTART VERIFICATION

Startup claim, JobRunner, and controlled iteration

Do not restore interrupted stacks; create new Jobs after restart, without ordinary Scheduler control.

  • self_update/recovery.py: system gates, lease, stable Job ID, exactly-once dispatch
  • worker/runner.py: startup hook after JobRunner ready and before ordinary Scheduler
  • agent/job/runner.py: source/authority/tool/model snapshot
  • self_update/iteration.py: attempt budget, scope diff, stop conditions
  • RED: repeated startup, expired lease, orphan verifier, missing provider, three-attempt exhaustion, expanded scope
TASK F · UI AND FULL ACCEPTANCE

Result delivery and App verification

Verification and recovery use application services; conversations retain ordinary messages and tool results.

  • Read-only status tools and durable result delivery
  • Desktop reopen locates origin session
  • Constrained real UI/API/CLI/test evidence
  • Python/Web/CLI/Desktop/full gates, docs, and three default-App scenarios

Task F dependencies and verification scope

Subtask and target location
Deliverable
Required acceptance
F1 · self_update/projection.py , Web self-updates routes, existing status tool, and Running route
First implement same-origin allowlisted projection, paginated history, and permission contract, without new control entries.
Public API/tools: cross-session rejection, no credential leaks, readable terminal states, read queries never write, corrupt state not disguised as empty, distinct candidate/running revisions, old recovery evidence cannot override new state.
F2 · Existing Job result delivery
Normal durable message delivery; no dedicated chat update controls or history polling.
Results preceding subscriptions, closed windows, crash after delivery, repeated recovery, late responses after session switches, awaiting approval/rollback/manual recovery, terminal history, keyboard, and narrow screens. Run real component behavior tests, not source-string checks.
F3 · apps/desktop/main.js, worker-start-url.js and startup handoff
Depends on F1/F2; update-bound one-time reopen and original-session load ACK, preserving ordinary startup.
Real route loading, wrong owner/session, expired intent, auth failure, lost ACK, multiple windows, manual-navigation precedence; no second verifier or automatic session mutation.
F4 · self_update/verifier_config.py , existing constrained-observation/signature protocol, and App/test adapters
Depends on reviewed restart verification and F3; freeze acceptance plans and provide real UI/API/CLI/test evidence. Workflow stage prompts share goal, assertions, and budget; execution enforces tool permissions.
Real main-App window identification/screenshots, approved interaction, actual CLI checks; reject arbitrary JS/CDP/URL/shell, unauthorized writes, cross-session access, identity changes, forged receipts, and replay. Old configurations gain no permissions; missing evidence/cleanup failure cannot pass.
F5 · Mainline integration, product docs, and release acceptance
Depends on F1–F4; synchronize this page and English/Chinese product docs, preserve/check architecture diagrams, and reconcile the complete objective with implementation.
Full Python, contracts, Web, CLI, Desktop, packaging, docs/checklinks, and combined-commit verification. Then update the default App only using scripts/refresh-local-app.sh and verify success, pre-activation failure, and post-activation rollback. Record local commit, main, remote, and installed identity separately.

Before each subtask, create an isolated worktree from an immutable reviewed commit and specify exact files, RED, and full test commands. Commit independent passing changes promptly; review pass is not release pass. Verified baseline failures remain failed, never substituted with smaller suites. Record machine docs checks, browser interaction, and desktop/narrow-screen visuals separately; add no duplicate design document.

14

Product-level completion conditions

Mark in-conversation self-update implemented only after every condition holds.

Real chat entry

Owner requests a change in the default App; Agent edits/tests/commits a worktree and invokes the dedicated tool, without developer-run scripts.

Immutable candidate

Request, artifact, and live revision bind the same SHA; dirty worktree, HEAD drift, and version drift fail before stopping the old instance.

Process boundary

Supervisor survives worker/App shutdown; current turn is durable without permanently running nodes.

No concurrent-work loss

Other running work causes waiting or update abort, never implicit cancellation; maintenance does not lose submitted input.

Complete artifact

App bundle, embedded Python, Web/docs, Programs, Browser assets, and capability manifest all come from the candidate and pass packaged smoke.

live identity

After restart, the default-18100 owner-auth challenge proves candidate revision; anonymous health alone is insufficient.

Automatic acceptance

New worker creates exactly one self_update_verify Job; every required assertion has a result/evidence, otherwise inconclusive.

Scheduling isolation

Ordinary Scheduler stoppage, delay, or duplicate ticks do not affect acceptance. Worker restart, lease takeover, and orphan reconciliation still permit at most one verifier per attempt.

Controlled iteration

Default reauthorization each time; bounded_auto strictly enforces attempts, total deadline, paths, and prohibited changes, pausing on any violation.

Automatic restoration

System-gate, goal-acceptance, and timeout failures restore previous.app with verifiable worker revision and original-session results.

Crash recovery

Simulated process/machine restarts in READY, ACTIVATING, and VERIFYING recover deterministic terminal states without duplicate activation or Agent turns.

Repository and visible state agree

Design, tests, reviews, candidate commit, App source marker, live worker identity, and UI verdict reflect one implementation state.

15

Implementation-status appendix

This section describes mainline/installed-product integration. Feature-branch implementation/review evidence is separate and does not mean product availability; current gaps never weaken the target contract.

Implemented foundation

Agent edits and worktrees

File/shell/worktree tools, approval, sandbox, read-before-edit, change attribution, and session DAG already exist.

Implemented foundation

Complete build and packaged smoke

Desktop runtime preparation, Electron builder, runtime manifest, and isolated smoke already exist.

Implemented foundation

App installation failure recovery

Existing install-app transactions preserve previous App and restore original state on activation/startup failure.

Implemented foundation

Updater, Scheduler, and JobRunner

Reuse installation detection, source probes, Job persistence, and orphan reconciliation. Startup claims use JobRunner; ordinary Scheduler controls neither updates nor fallback acceptance.

Integrated candidate · release verification pending

Durable self-update protocol

The combined candidate includes state machine, single-active lock, durable release, maintenance, external supervisor, deferred commit/rollback, owner repair, and App-independent login recovery. Offline private inputs, fixed-SHA Electron distribution, single-port packaged-worker smoke, fixed icon rendering, and trusted-runtime browser probes passed repaired full actual packaging. Browser probes execute only revalidated saved controller runtime inaccessible to candidate writes; the controller never executes writable build runtime. Independent reviews, final combined gates, mainline release, and actual install/recovery acceptance remain incomplete.

Integrated candidate · release pending

Automatic acceptance and session recovery

Startup claim, stable self_update_verify Job, signed results, rollback on goal failure, durable delivery, and Desktop reopen are integrated. An old App missing packaging protocols first requires explicit full refresh; source tests do not prove installed-App restart, original-session ACK, or automatic acceptance.

Integrated candidate · release pending

Diagnosis and automatic repair

Post-rollback diagnosis, constrained edit proposals, isolated worktree/commit, frozen tests, reapproval, and bounded_auto are integrated. Restart never resets attempts/deadlines; full combined checks and real update sequences still await acceptance, without replacing original failure conclusions.

Integrated candidate · release pending

Result delivery and constrained observation

Ordinary result delivery, original-session navigation, and signed HTTP, CLI/test, screenshot, scroll, and view checks are integrated. Chat embeds no update-management interface; temporary test-object operations are unsupported. Capabilities are limited to frozen checks; arbitrary UI actions and real-data mutation are unauthorized. Overall completion remains pending the three default-App scenarios and actual goal evidence.

openprogram/programs/tools/system/self_update/Owner tools, prepare/retry approval, and observation entry openprogram/self_update/control/supervisor.pyExternal transaction control, system checks, and rollback openprogram/self_update/verification/verifier_config.pyFrozen configurations, stage prompts, and tool allowlists openprogram/self_update/verification/verification_plan.pyFixed checks, resource budgets, and UI protocol capabilities openprogram/self_update/repair/next_candidate.pyNext-candidate authorization, evidence, budget, and submission openprogram/self_update/control/projection.pyShared read-only state for tools, Web, and Running tests/component/self_update/Control-protocol, acceptance, recovery, and iteration tests; not actual App-release proof apps/desktop/scripts/package-and-install-app.shComplete App build, smoke, and installation entry apps/desktop/scripts/install-app.shTransactional installation, previous App, and failure recovery scripts/release/smoke-packaged-runtime.shPackaged runtime and Web smoke apps/cli/.../commands/upgrade.pySource checkout probe, restart, and revision verification openprogram/worker/lifecycle.pyworker stop/start/restart openprogram/agent/job/store.pyJob persistence and worker-lost recovery semantics openprogram/agent/job/runner.pyOne-shot verification Job execution and resource governance openprogram/scheduler/service.pyOrdinary Scheduler contract; self-update does not depend on this entry openprogram/updater/__init__.pyInstallation detection and existing prohibition on startup-applied updates apps/server/.../_exec_dag.pystartup interrupted-run reconciliation openprogram/agent/permissions/approval.pyOwner, source, tool approval, and hard constraints

Conversation boundary

Opening, switching or reconnecting a conversation does not query software-update history. Normal tool output and existing result delivery remain available. Software-update loading, status, retry forms and recovery notices are absent from the chat UI. The production App does not mount a temporary verification rename dialog. Verification rejects test_object interactions before any UI request; screenshot, bounded scroll and perspective checks remain supported.

Acceptance: normal chat and App shell mount without software-update UI or history requests; desktop session acknowledgement still works; install and rollback behavior remains unchanged.