fix(agent): apply execution policy to existing workspaces

This commit is contained in:
Xubin Ren
2026-07-24 19:25:52 +08:00
parent 51f11a8548
commit e260d9b31c
6 changed files with 69 additions and 32 deletions
+5
View File
@@ -169,6 +169,11 @@ class ContextBuilder:
file_path = root / filename
if file_path.exists():
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():
continue
if filename in self._SKIPPABLE_DEFAULTS and self._is_template_content(
-13
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.
- 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
- 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.
+11 -10
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.
- 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.
- 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
@@ -29,16 +35,11 @@
- 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
visual evidence reaches the model; do not guess text, measurements, or recovered data.
- Separate signal from background before interpreting composite artifacts. Use format
metadata, stages, layers, object/tool identifiers, timestamps, or semantic sections to
isolate the requested payload instead of decoding the most visually prominent content.
- Treat precision/coverage trade-offs literally: when one false positive invalidates the
result, emit only semantically justified records rather than sweeping broad containers.
- 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.
- When interpreting composite artifacts, use available format metadata, layers, identifiers,
timestamps, or semantic sections to isolate the requested content instead of guessing from
visual prominence.
- Never invent missing records or measurements. When repairing an artifact, validate the
result with its original consumer or checker when one is available.
- 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 `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.
+18
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.
+3 -1
View File
@@ -226,7 +226,9 @@ class TestBundledToolContract:
assert "apply_patch" in content
assert "acceptance criteria into concrete checks" 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 "## Messaging and Media" in content
assert "## Scheduling and Background Work" in content
+32 -8
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:
"""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
workspace = _make_workspace(tmp_path)
@@ -224,10 +224,29 @@ def test_execution_rules_in_system_prompt(tmp_path) -> None:
prompt = builder.build_system_prompt()
assert "clear user request" 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
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:
"""Identity template should not contain behavioral rules or hardcoded name."""
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
def test_default_soul_template_contains_execution_rules() -> None:
"""Default SOUL.md template must contain execution rules with act/plan layering."""
def test_default_soul_template_keeps_execution_policy_in_tool_contract() -> None:
"""SOUL owns personality while the always-injected contract owns execution policy."""
soul = (pkg_files("nanobot") / "templates" / "SOUL.md").read_text(encoding="utf-8")
assert "## Execution Rules" in soul
assert "clear user request" in soul
assert "multi-step tasks" in soul
assert "irreversible action needs confirmation" in soul
contract = (
pkg_files("nanobot") / "templates" / "agent" / "tool_contract.md"
).read_text(encoding="utf-8")
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: