Skip to content

[codex] fix goal control state and turn provenance#1201

Draft
chovy-ai wants to merge 1 commit into
mainfrom
codex/goal-control-root-fix
Draft

[codex] fix goal control state and turn provenance#1201
chovy-ai wants to merge 1 commit into
mainfrom
codex/goal-control-root-fix

Conversation

@chovy-ai

@chovy-ai chovy-ai commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Issue

Calling /goal and immediately clearing it could leave the first response permanently thinking, keep the session running, and leave /goal clear stuck at planning the next step.

The user-visible failure came from modeling Goal control as a specially shaped Turn. The old path could allocate a local Turn ID before the provider had started any Turn. If the Goal was cleared before provider activation, that phantom Turn never received a matching terminal lifecycle event.

Root cause

Goal is session-owned, long-running state that can produce zero or more provider Turns. It is not itself a Turn. The previous implementation mixed these identities and did not have durable operation/revision/provenance fences for delayed provider observations, retries, restarts, or clear-and-recreate races.

Fix

This PR implements the three-stage Goal/Turn design documented in docs/specs/2026-07-15-agent-goal-control-design.md:

  • routes typed Goal commands through one durable session-level control path without preallocating a Turn;
  • adds durable Goal state, Goal control operations/outbox, a per-session Goal actor, revision and repair fences, recovery, reconciliation, and calibration APIs;
  • separates provider acknowledgement, execution, and convergence phases;
  • creates Turns only from real provider lifecycle observations and persists immutable Turn origin plus Goal operation/revision/repair provenance;
  • adds first-class session audit events so turnless Goal control history never enters Turn lifecycle logic;
  • adds exact durable Codex Goal-generation provenance with session-incarnation fencing, collision tombstones, restart-safe lookup, and fail-closed handling;
  • makes reconcile durable and recoverable through a two-phase inbox, leases, bounded retries, terminal fences, and adapter-owned provider capabilities;
  • preserves main's provider-native root/child session lifecycle: provider terminals settle canonical root Turns transactionally without duplicate terminal events;
  • keeps Claude clear precise: durable Goal clear cancels only its known Goal arm, while legacy compatibility paths still cancel exact registered provider Turn IDs.

User impact

/goal set followed immediately by /goal clear now converges without a phantom Turn, permanent thinking state, or session-running leak. Delayed provider work cannot inherit a newer Goal identity, resurrect a cleared Goal, or cancel an unrelated user Turn.

Validation

  • repository pre-push check:full: 18 tasks passed;
  • affected Go packages passed, including store, activity, runtime, service, workspace data, and API;
  • Goal-focused race tests passed for store, runtime, and service;
  • agent activity core: 183/183;
  • Claude SDK sidecar: 78/78;
  • tuttid TypeScript client: 53/53;
  • Agent GUI: 1617/1617;
  • TypeScript typecheck: 27 packages;
  • Go and TypeScript lint: passed;
  • API, event protocol, Codex protocol, and provider catalog generated checks: passed;
  • git diff --check: passed.

Deferred non-blocking hardening

  • add server routes and an end-to-end contract test before enabling the future remote HTTP provenance reporter path;
  • add bounded incremental GC for completed/failed reconcile inbox rows while preserving the request replay window and terminal audit.

Summary by cubic

Fixes phantom Turns and stuck “thinking” by making Goal a session-level entity and separating Goal control from Turn lifecycle. Adds durable Goal provenance and session audit events so /goal set followed by /goal clear converges without leaks or orphaned Turns.

  • New Features

    • Added a typed Goal control path that never preallocates a Turn; sendWorkspaceAgentSessionInput may return { kind: "goalControl" } with no turn/turnId.
    • Introduced durable Goal state, control operations, revision/repair fences, and reconcile APIs; new getWorkspaceAgentSessionGoal and reconcileWorkspaceAgentSessionGoal transport methods.
    • Turns carry immutable origin and optional Goal provenance (sourceGoalOperationId, sourceGoalRevision, sourceGoalRepairEpoch); reducers preserve provenance across snapshots in @tutti-os/agent-activity-core, daemon/runtime, and store-sqlite.
    • Added first-class session_audit events for turnless Goal history; inline parser and report coalescer handle audits; store enforces that only session_audit messages may be turnless.
    • Updated adapters and runtime:
      • Codex appserver and Claude sidecar announce Goal arms, emit turn origin, and quiesce Goal Turns with bounded retries and grace windows.
      • Implemented durable Goal provenance binding/lookup with controller/ledger; unowned provider Turns wait briefly for goal evidence, then adopt or reconcile; ambiguity creates tombstones and fails closed.
      • Unified Goal adapter surface with GoalCapabilities and ApplyGoal(...); controller exposes goal capabilities and reconcile helpers.
    • Desktop and GUI:
      • Adapter and services return discriminated results with resultKind and track goal-only sends in diagnostics.
      • Activation supports initialTurnExpected to send goal controls without expecting a Turn.
      • Tests and projections updated to require/propagate origin on Turns.
  • Migration

    • Handle sendWorkspaceAgentSessionInput returning kind: "goalControl"; do not assume a Turn exists after Goal commands; track resultKind in diagnostics.
    • Treat goal banner actions as session control, not prompts; when activating for goal-only actions, set initialTurnExpected: false.
    • Expect origin on all Turns and propagate it in models, reducers, events, and UI.
    • Only allow turnless message updates for session_audit; all other messages must reference a Turn.
    • Use the new Goal APIs (getWorkspaceAgentSessionGoal, reconcileWorkspaceAgentSessionGoal) instead of routing through the prompt pipeline; provider adapters should implement GoalCapabilities/ApplyGoal and durable provenance binding.

Written for commit 58a263d. Summary will update on new commits.

Review in cubic

@chovy-ai
chovy-ai force-pushed the codex/goal-control-root-fix branch from 70077fe to 58a263d Compare July 16, 2026 03:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant