Skip to content

refactor(agent): gate sustained goals behind explicit /goal - #4844

Merged
Re-bin merged 13 commits into
mainfrom
codex/goal-runtime-main
Jul 11, 2026
Merged

Re-bin merged 13 commits into
mainfrom
codex/goal-runtime-main

Conversation

@chengyongru

@chengyongru chengyongru commented Jul 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR refactors the existing sustained-goal workflow without adding a new top-level capability.

  • Replace the legacy long_task / complete_goal contract and built-in long-goal skill with create_goal / update_goal plus a compact runtime prompt template.
  • Require a real user /goal <task> turn before goal creation or replacement; ordinary messages, automation turns, internal continuations, and stale history cannot grant that permission.
  • Revoke the request-local mutation scope after complete, cancel, or block, while allowing deliberate replacement during the active /goal run.
  • Preserve persisted goal metadata, runtime visibility, WebUI goal-state events, compaction behavior, and internal continuation limits.
  • Reset continuation rounds when a goal is created or replaced, and roll back in-memory metadata if persistence fails.
  • Keep host-added goal guidance out of saved user history without truncating user-authored text that contains runtime labels.

Architecture

  • The /goal command adds a goal-owned permission context through the generic turn_scopes seam.
  • AgentLoop only enters and restores generic turn scopes; goal-specific permission state stays outside the core request-context module.
  • Runtime guidance is derived independently from command metadata and cannot grant execution permission.
  • Goal tools continue to read the authoritative RequestContext for routing and session ownership.

Compatibility

  • Existing persisted thread_goal metadata remains readable and is migrated to goal_state on mutation.
  • Tool schemas stay stable across runner iterations.
  • The removed skill is replaced by runtime guidance that preserves the durable-objective and clarification protocol.

Verification

  • uv run ruff check passed for the changed Python surfaces.
  • 274 focused command, runner, request-context, goal lifecycle, persistence, and concurrency tests passed after merging the latest main.
  • The final PR branch contains one commit on top of the latest main.

@chengyongru chengyongru left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from nanobot

Clean refactor of the sustained-goal surface into runtime-gated create_goal/update_goal with a per-run ToolRegistryView. The within-run lifecycle (create -> update -> complete hiding goal tools again) is well covered by test_goal_tool_visibility_changes_within_runner_turn, and backward compatibility with existing active goals is preserved (same GOAL_STATE_KEY / status == "active" contract).

Two minor notes below; neither blocks merge.

  • The repo-level AGENTS.md still describes the old surface: line 40 references long-running tasks / sustained goals (`long_task.py`) (now goal.py) and line 49 lists the long-goal skill (removed by this PR). Worth updating in this PR so the architecture doc stays accurate.
  • Inline comment on the goal runtime section: it can go stale relative to the tool view within a single turn.

Comment thread nanobot/agent/context.py Outdated
return None
objective_guidance = render_template("agent/goal_objective.md", strip=True)
template = "agent/goal_active.md" if mode == "active" else "agent/goal_create.md"
return render_template(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal runtime section is rendered once here at message-build time (in _state_build), but the ToolRegistryView returned by _goal_filtered_tools is re-evaluated every LLM round against the live session metadata. So within a single /goal turn, after create_goal fires the system prompt still carries the goal_create.md guidance ("call create_goal") while create_goal is already hidden and only update_goal is visible. The mismatch is low-impact (the create_goal tool result and the visible update_goal tool steer the model correctly, and the section is rebuilt correctly on the next turn / continuation), but if you want the prompt and tool list to stay in lockstep mid-turn, the section would need to be recomputed per round like the tool view is.

@chengyongru chengyongru left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from nanobot

I found two actionable runtime-gating issues. The initial clarification path drops out of goal creation entirely, and filtered registry errors can reveal goal tools that are meant to stay hidden on normal turns.

Comment thread nanobot/templates/agent/goal_create.md Outdated

If the objective is clear enough, call `create_goal` with a concise, durable
objective before doing substantial execution work. If it is too ambiguous to form
a safe objective, ask a clarifying question instead of creating a goal.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This clarification branch cannot resume goal creation. create mode is derived only from the current inbound message metadata; because no goal or pending-create state is persisted when the assistant asks this question, the user's next ordinary reply is classified as normal and exposes neither goal tool. The user therefore has to know to repeat /goal, even though this prompt asks them only to answer the clarification, so ambiguous /goal requests fall out of the lifecycle.

Comment thread nanobot/agent/tools/registry.py Outdated
f"Error: Tool '{name}' not found. Available: {', '.join(self.tool_names)}"
)
)
return self._registry.prepare_call(name, params)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delegating unknown names to the underlying registry bypasses the view's filtered error surface. On a normal turn, a common near miss such as createGoal passes _allowed because only the exact create_goal name is gated; the underlying registry then suggests create_goal and lists both hidden goal tools as available. They still cannot execute through the view, but the error teaches the model about capabilities this view is meant to hide and can trigger pointless retries.

@chengyongru

Copy link
Copy Markdown
Collaborator Author

Pushed two maintainer fixes for the review findings:

  • 07dbe708 keeps the goal prompts honest across clarification and within-turn state changes. Ambiguous requests now explicitly ask for a fresh /goal, and the latest goal tool result is authoritative after create/complete/cancel/block transitions.
  • b5808a9c builds ToolRegistryView missing-tool suggestions and availability lists from visible tools only, so near-miss calls cannot reveal runtime-gated goal tools.

Verification: focused goal/context/runner/registry suite (174 passed) and Ruff both pass locally.

@chengyongru chengyongru changed the title Gate sustained goals behind explicit runtime mode Require explicit user opt-in for sustained goals Jul 10, 2026
@chengyongru

Copy link
Copy Markdown
Collaborator Author

Reworked the PR to the conservative opt-in design in ee6406f3:

  • Removed runtime-specific system prompts and ToolRegistryView; normal, goal, and completed turns now keep the same prompt/tool schema for KV-cache reuse.
  • Kept both goal tools available, but moved the guard to create_goal: it now requires an exact current-message trigger_quote containing goal.
  • Added end-to-end coverage for a prior planning discussion followed by "用goal执行刚才讨论的方案", plus negative coverage for ordinary implementation requests.

Focused cross-module verification passes locally (291 passed; Ruff clean).

@chengyongru chengyongru changed the title Require explicit user opt-in for sustained goals fix(agent): require explicit user opt-in for sustained goals Jul 10, 2026
@chengyongru
chengyongru force-pushed the codex/goal-runtime-main branch from 5c5f472 to af9b0be Compare July 10, 2026 08:07
@chengyongru
chengyongru marked this pull request as draft July 10, 2026 08:07
@chengyongru

Copy link
Copy Markdown
Collaborator Author

Depends on #4868. Keep this PR in draft and merge it only after #4868 lands in main.

@chengyongru
chengyongru force-pushed the codex/goal-runtime-main branch 2 times, most recently from 5f9604f to 1884d99 Compare July 10, 2026 09:58
@chengyongru chengyongru changed the title fix(agent): require explicit user opt-in for sustained goals fix(agent): require slash authorization for sustained goals Jul 10, 2026
@chengyongru
chengyongru marked this pull request as ready for review July 11, 2026 06:10
@chengyongru
chengyongru force-pushed the codex/goal-runtime-main branch from 64dad04 to f6573d8 Compare July 11, 2026 08:51
@chengyongru chengyongru changed the title fix(agent): require slash authorization for sustained goals refactor(agent): gate sustained goals behind explicit /goal Jul 11, 2026

@Re-bin Re-bin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review generated by Codex.

One small follow-up: internal continuations currently retain goal_requested / original_command, so a synthetic continuation still receives fresh /goal creation guidance even though it has no mutation scope. The permission guard prevents an unauthorized state change, but this can still prompt redundant create_goal attempts and extra token use. Could we strip those command-only markers when building continuation metadata and add a regression test for that path?

@chengyongru
chengyongru force-pushed the codex/goal-runtime-main branch from f6573d8 to 87b0a0c Compare July 11, 2026 09:22
@chengyongru
chengyongru force-pushed the codex/goal-runtime-main branch from 87b0a0c to 7e9269f Compare July 11, 2026 10:06
Replace the legacy long-goal skill contract with command-scoped goal tools and runtime guidance. Keep goal state durable across continuations while restricting create and replace mutations to explicit user /goal turns.

@Re-bin Re-bin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. This now lands as one coherent change: sustained goals are explicitly gated behind /goal, while the dependent runtime-context layer keeps ordinary prompts stable and only opts into model-visible state for active goals and attached CLI Apps.

The follow-up review closed the lifecycle boundaries around internal context: continuations cannot inherit fresh /goal authority, forks do not carry source-session runtime blocks, consolidation/Dream and raw archives do not absorb host-only guidance, and SDK restore validates before mutation.

The combined exact head passed the full GitHub Actions matrix on Ubuntu and Windows (Python 3.13/3.14) plus WebUI. Focused local tests, WebUI tests, and the production build also passed.

@Re-bin
Re-bin merged commit 01e4f37 into main Jul 11, 2026
5 checks passed
@chengyongru
chengyongru deleted the codex/goal-runtime-main branch July 14, 2026 05:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p1 Important user-impacting issue or project priority with bounded impact or a reasonable workaround. refactor valid

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants