fix(agent): apply execution policy to existing workspaces

This commit is contained in:
Xubin Ren 2026-07-24 17:16:15 +08:00
parent 51f11a8548
commit e260d9b31c
6 changed files with 69 additions and 32 deletions

View File

@ -169,6 +169,11 @@ class ContextBuilder:
file_path = root / filename file_path = root / filename
if file_path.exists(): if file_path.exists():
content = file_path.read_text(encoding="utf-8") content = file_path.read_text(encoding="utf-8")
if filename == "SOUL.md" and self._is_template_content(
content,
"legacy/SOUL.md",
):
content = load_bundled_template("SOUL.md") or content
if not content.strip(): if not content.strip():
continue continue
if filename in self._SKIPPABLE_DEFAULTS and self._is_template_content( if filename in self._SKIPPABLE_DEFAULTS and self._is_template_content(

View File

@ -9,16 +9,3 @@ I am nanobot 🐈, a personal AI assistant.
- Say what I know, flag what I don't, and never fake confidence. - Say what I know, flag what I don't, and never fake confidence.
- Stay friendly and curious — I'd rather ask a good question than guess wrong. - Stay friendly and curious — I'd rather ask a good question than guess wrong.
- Treat the user's time as the scarcest resource, and their trust as the most valuable. - Treat the user's time as the scarcest resource, and their trust as the most valuable.
## Execution Rules
- Treat a clear user request as authorization to complete it in the current turn.
- For multi-step tasks, outline the plan briefly and then execute it immediately. Wait only
when an irreversible action needs confirmation or an essential choice cannot be resolved
from the available context and tools.
- Read before you write — do not assume a file exists or contains what you expect.
- When information is missing, look it up with tools first. Only ask the user when tools cannot answer.
- For coding and technical tasks, continue through implementation and verification; do not
stop at a plan, diagnosis, or plausible-looking output.
- Prefer evidence over guesses. Derive answers from the supplied artifacts, inspect generated
visualizations with `read_file`, and run the strongest relevant local checks before finishing.

View File

@ -9,6 +9,12 @@
- After meaningful changes, verify the result with the smallest reliable check: re-read changed state, run targeted tests, or inspect command output. - After meaningful changes, verify the result with the smallest reliable check: re-read changed state, run targeted tests, or inspect command output.
- When tools are needed before answering, do not include the final answer with the tool calls. Wait for the tool results, then answer once. - When tools are needed before answering, do not include the final answer with the tool calls. Wait for the tool results, then answer once.
- Respect safety and workspace-boundary errors as real limits, not obstacles to bypass. - Respect safety and workspace-boundary errors as real limits, not obstacles to bypass.
- Treat a clear user request as authorization to complete it in the current turn.
- For multi-step tasks, outline the plan briefly and then execute it. Wait only when an
irreversible action needs confirmation or an essential choice cannot be resolved from the
available context and tools.
- For coding and technical tasks, continue through implementation and verification; do not
stop at a plan, diagnosis, or plausible-looking output.
## Discovery and Reading ## Discovery and Reading
@ -29,16 +35,11 @@
- For binary, numerical, and visual artifacts, create a deterministic inspectable - For binary, numerical, and visual artifacts, create a deterministic inspectable
representation when useful. Render plots or images to PNG and call `read_file` on them so representation when useful. Render plots or images to PNG and call `read_file` on them so
visual evidence reaches the model; do not guess text, measurements, or recovered data. visual evidence reaches the model; do not guess text, measurements, or recovered data.
- Separate signal from background before interpreting composite artifacts. Use format - When interpreting composite artifacts, use available format metadata, layers, identifiers,
metadata, stages, layers, object/tool identifiers, timestamps, or semantic sections to timestamps, or semantic sections to isolate the requested content instead of guessing from
isolate the requested payload instead of decoding the most visually prominent content. visual prominence.
- Treat precision/coverage trade-offs literally: when one false positive invalidates the - Never invent missing records or measurements. When repairing an artifact, validate the
result, emit only semantically justified records rather than sweeping broad containers. result with its original consumer or checker when one is available.
- For scientific fitting, preserve the supplied independent coordinate and establish its
units or calibration before optimization. Fit plausible physical models and baselines,
inspect residuals, and sanity-check reported parameters in the original requested units.
- For data recovery, never invent missing records. Prove that the repaired artifact itself
can be consumed by the original software against an untouched copy of its base data.
- Use `apply_patch` as the default code editing tool, especially for multi-file changes, structural edits, generated code, moves, adds, or deletes. - Use `apply_patch` as the default code editing tool, especially for multi-file changes, structural edits, generated code, moves, adds, or deletes.
- Use `apply_patch dry_run=true` when the patch is uncertain and you want validation plus a change summary before writing. - Use `apply_patch dry_run=true` when the patch is uncertain and you want validation plus a change summary before writing.
- Use `edit_file` only for small exact replacements in one file, with `old_text` copied from `read_file`; when editing a specific numbered line, pass that exact line as `line_hint`; add `occurrence` or `expected_replacements` when ambiguity matters. - Use `edit_file` only for small exact replacements in one file, with `old_text` copied from `read_file`; when editing a specific numbered line, pass that exact line as `line_hint`; add `occurrence` or `expected_replacements` when ambiguity matters.

View File

@ -0,0 +1,18 @@
# Soul
I am nanobot 🐈, a personal AI assistant.
## Core Principles
- Solve by doing, not by describing what I would do.
- Keep responses short unless depth is asked for.
- Say what I know, flag what I don't, and never fake confidence.
- Stay friendly and curious — I'd rather ask a good question than guess wrong.
- Treat the user's time as the scarcest resource, and their trust as the most valuable.
## Execution Rules
- Act immediately on single-step tasks — never end a turn with just a plan or promise.
- For multi-step tasks, outline the plan first and wait for user confirmation before executing.
- Read before you write — do not assume a file exists or contains what you expect.
- When information is missing, look it up with tools first. Only ask the user when tools cannot answer.

View File

@ -226,7 +226,9 @@ class TestBundledToolContract:
assert "apply_patch" in content assert "apply_patch" in content
assert "acceptance criteria into concrete checks" in content assert "acceptance criteria into concrete checks" in content
assert "visual evidence reaches the model" in content assert "visual evidence reaches the model" in content
assert "precision/coverage trade-offs" in content assert "clear user request as authorization" in content
assert "Never invent missing records or measurements" in content
assert "scientific fitting" not in content
assert "## Web and External Information" in content assert "## Web and External Information" in content
assert "## Messaging and Media" in content assert "## Messaging and Media" in content
assert "## Scheduling and Background Work" in content assert "## Scheduling and Background Work" in content

View File

@ -214,7 +214,7 @@ def test_partial_dream_processing_shows_only_remainder(tmp_path) -> None:
def test_execution_rules_in_system_prompt(tmp_path) -> None: def test_execution_rules_in_system_prompt(tmp_path) -> None:
"""Execution rules should appear in the system prompt via default SOUL.md.""" """Execution rules should appear in the system prompt via the default templates."""
from nanobot.utils.helpers import sync_workspace_templates from nanobot.utils.helpers import sync_workspace_templates
workspace = _make_workspace(tmp_path) workspace = _make_workspace(tmp_path)
@ -224,10 +224,29 @@ def test_execution_rules_in_system_prompt(tmp_path) -> None:
prompt = builder.build_system_prompt() prompt = builder.build_system_prompt()
assert "clear user request" in prompt assert "clear user request" in prompt
assert "multi-step tasks" in prompt assert "multi-step tasks" in prompt
assert "Read before you write" in prompt assert "read-only discovery before writes" in prompt
assert "verify the result" in prompt assert "verify the result" in prompt
def test_execution_rules_reach_existing_workspace_soul(tmp_path) -> None:
"""An untouched legacy SOUL is upgraded in memory without overwriting the file."""
workspace = _make_workspace(tmp_path)
legacy_soul = (
pkg_files("nanobot") / "templates" / "legacy" / "SOUL.md"
).read_text(encoding="utf-8")
legacy_rule = "For multi-step tasks, outline the plan first and wait for user confirmation."
soul_path = workspace / "SOUL.md"
soul_path.write_text(legacy_soul, encoding="utf-8")
builder = ContextBuilder(workspace)
prompt = builder.build_system_prompt()
current_rule = "Treat a clear user request as authorization"
assert legacy_rule not in prompt
assert current_rule in prompt
assert soul_path.read_text(encoding="utf-8") == legacy_soul
def test_identity_has_no_behavioral_instructions(tmp_path) -> None: def test_identity_has_no_behavioral_instructions(tmp_path) -> None:
"""Identity template should not contain behavioral rules or hardcoded name.""" """Identity template should not contain behavioral rules or hardcoded name."""
workspace = _make_workspace(tmp_path) workspace = _make_workspace(tmp_path)
@ -250,13 +269,18 @@ def test_system_prompt_does_not_warn_about_message_time_markers(tmp_path) -> Non
assert "Message Time" not in prompt assert "Message Time" not in prompt
def test_default_soul_template_contains_execution_rules() -> None: def test_default_soul_template_keeps_execution_policy_in_tool_contract() -> None:
"""Default SOUL.md template must contain execution rules with act/plan layering.""" """SOUL owns personality while the always-injected contract owns execution policy."""
soul = (pkg_files("nanobot") / "templates" / "SOUL.md").read_text(encoding="utf-8") soul = (pkg_files("nanobot") / "templates" / "SOUL.md").read_text(encoding="utf-8")
assert "## Execution Rules" in soul contract = (
assert "clear user request" in soul pkg_files("nanobot") / "templates" / "agent" / "tool_contract.md"
assert "multi-step tasks" in soul ).read_text(encoding="utf-8")
assert "irreversible action needs confirmation" in soul
assert "## Execution Rules" not in soul
assert "clear user request" not in soul
assert "clear user request" in contract
assert "multi-step tasks" in contract
assert "irreversible action needs confirmation" in contract
def test_channel_format_hint_telegram(tmp_path) -> None: def test_channel_format_hint_telegram(tmp_path) -> None: