Skip to content

Home channel, plus /sethome, /whoami and /pause - #635

Merged
jamiepine merged 14 commits into
mainfrom
jamiepine/commands-and-home-channel
Aug 10, 2026
Merged

Home channel, plus /sethome, /whoami and /pause#635
jamiepine merged 14 commits into
mainfrom
jamiepine/commands-and-home-channel

Conversation

@jamiepine

Copy link
Copy Markdown
Member

Builds out the home channel — the instance's default outbound destination, the one conversation the agent can reach when nothing is in scope — and adds the first commands that need it.

Phases 1–3 of docs/design-docs/home-channel.md. Phase 4 (autonomous outreach) is deliberately not here; see below.

Home channel

WakeDef.delivery_target has been validated, persisted, and read by nothing since it landed, because there was no sensible default behind it. This gives it one.

  • StorageSettingsStore accessors carrying an explicit/implicit flag. adopt_home_channel is the single invariant point: an implicit home never overwrites a claimed one, so first run wins until a principal sets it deliberately.
  • Resolutionresolve_home_target() implements wake target → home → nothing. The third branch is load-bearing: unresolvable means silence, never "the most recent channel we saw," which is how an agent posts a private observation into a group.
  • Config seeddefaults.home_channel seeds an instance that ships pre-configured, rejected at load if it isn't adapter:target. The database owns it from then on, so a reload never clobbers a runtime change.
  • Setting it — a set_home_channel tool on user channels resolving the chat it was called from, with /sethome as a second entry point over the same handler. Authority-gated, and validated at set time so a home the agent can't reach fails while the user is still looking at the reply.
  • First run — the first user conversation to complete a turn adopts the home and says so in that chat. The destination is never a default the user discovers by receiving something unexpected.
  • Surface — autonomy status carries the resolved home; the dial card shows where proactive messages land, or that findings are only being recorded. DELETE /agents/autonomy/home gives it up. There's no set-from-here counterpart on purpose — a home is claimed from the chat that should receive it, so the only thing a settings screen can offer is giving it up.

Commands

  • /whoami — answers what you may do here, not who you are. The authority split is otherwise invisible until someone hits a denial.
  • /pause [reason | off] — holds off on starting new work and survives restart, so an emergency stop isn't undone by a bounce. Gates three entry points: inbound messages in the router, autonomy runs, and cron fires.

Two details worth knowing about /pause:

Commands dispatch before the router gate, so /pause off and /status still land while paused — the stop can always be undone from the chat that set it.

A cron fire skipped during a pause is dropped after its cursor has already advanced, rather than banked:

// Skip the fire while paused, after the cursor has already
// advanced: a pause drops the runs it covers rather than
// banking them into a burst at resume.
if let Some(reason) = context.deps.pause_reason() {
    continue;
}

Pause is per-agent, following the settings store it lives in. On a single-agent instance that's global; on a multi-agent one, pausing in one agent's chat won't quiet the others.

Control actions now receive their validated argument string — /pause is the first to need one.

Not in this PR

resolve_home_target() takes a wake target and is tested, but nothing passes one yet: the only caller that would is the autonomy send path, which is phase 4. Wiring sending without phase 4's level gating would let an observe-level instance start talking, which breaks mute-by-default. The autonomy channel still drains and drops its responses (src/agent/autonomy.rs).

Testing

cargo test --lib — 1110 passing. New coverage:

  • resolve_home_target across all three branches, including an unparseable wake target falling through to home
  • the adoption invariant: implicit loses to implicit, explicit replaces, implicit can never take it back
  • /status rendering for unset / adopted / explicit homes, and the pause line leading the block
  • /whoami describing one boundary from both sides

cargo clippy --lib --all-targets clean, bunx tsc --noEmit clean.

Adds the instance's default outbound destination — the one conversation
the agent can reach when nothing is in scope.

- SettingsStore accessors with an explicit/implicit flag. adopt_home_channel
  is the invariant point: an implicit home never overwrites a claimed one.
- resolve_home_target() implements the wake-target > home > nothing order.
  Unresolvable means silence, never a recently-seen channel.
- defaults.home_channel seeds an instance that ships pre-configured, rejected
  at load if it isn't adapter:target. The database owns it from then on.
- set_home_channel tool on user channels, resolving the chat it was called
  from, plus /sethome over the same handler. Authority-gated, validated at
  set time so an unreachable home fails while the user is still looking.
- /status reports the resolved home and marks an adopted one.
- The first user conversation to complete a turn adopts the home when
  nothing has claimed it, and says so in that chat — the destination is
  never a default the user finds out about by receiving something.
- Autonomy status carries the resolved home; the dial card shows where
  proactive messages land, or that findings are only recorded.
- DELETE /agents/autonomy/home gives it up. There is no set-from-here
  counterpart: a home is claimed from the chat that should receive it.
/whoami answers what the sender may do here rather than who they are —
the authority split is otherwise invisible until someone hits a denial.

/pause holds off on starting new work and survives restart, so an
emergency stop isn't undone by a bounce. It gates three entry points:
inbound messages in the router, autonomy runs, and cron fires. Commands
dispatch before the router gate, so /pause off and /status still land
while paused. A cron fire skipped during a pause is dropped after its
cursor advances rather than banked into a burst at resume.

Pause is per-agent, following the settings store it lives in.

Control actions now receive their validated argument string, which /pause
is the first to need.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 137152ce-faee-4e2a-af4b-8bbff1b0da6b

📥 Commits

Reviewing files that changed from the base of the PR and between 919d90a and b826d85.

📒 Files selected for processing (4)
  • prompts/en/autonomy_channel.md.j2
  • src/agent/autonomy.rs
  • src/commands/access.rs
  • src/commands/dispatch.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • prompts/en/autonomy_channel.md.j2
  • src/agent/autonomy.rs
  • src/commands/dispatch.rs

Walkthrough

The change adds persistent home-channel routing, pause controls, autonomy transcript continuity, authority-aware commands and tools, API status management, and interface controls. It also adds empty-instance autonomy guidance and home-channel configuration.

Changes

Home channel and autonomy

Layer / File(s) Summary
Autonomy and home-channel behavior contracts
docs/design-docs/autonomy.md, docs/design-docs/home-channel.md, prompts/en/autonomy_channel.md.j2, src/agent/autonomy.rs, src/prompts/engine.rs
Autonomy restores transcript continuity, journals outbound actions, records completion summaries, and handles empty instances with workspace inspection and memory recording.
Persistent settings and target resolution
src/settings/*, src/config/*, src/messaging/target.rs, src/lib.rs
Home-channel and pause state persist in settings. Configuration validates default targets. Target resolution applies wake-target precedence and atomic first-write adoption.
Control commands and pause lifecycle
src/commands/*, src/agent/channel.rs, src/cron/scheduler.rs, src/main.rs
The system adds /sethome, /pause, and /whoami, authority-aware dispatch, first-run home adoption, pause-aware execution, status output, and command argument handling.
Home-channel tool, API, and UI integration
src/tools/*, src/api/*, interface/src/api/*, interface/src/components/autonomy/*, interface/src/routes/*, prompts/en/tools/*, src/prompts/text.rs, tests/context_dump.rs
The tool sets the current channel as home. The API exposes and clears home status. The interface displays and clears the configured target. Tool registration uses sender authority.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the home-channel feature and the three related commands added by the pull request.
Description check ✅ Passed The description directly explains the home channel, related commands, pause behavior, scope, and testing for the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jamiepine/commands-and-home-channel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jamiepine
jamiepine marked this pull request as ready for review August 10, 2026 08:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (1)
src/settings/store.rs (1)

196-202: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use full names for closure bindings.

Rename t and v to target and value in the new home and pause accessors. These names violate the Rust path rule for descriptive variable names.

As per coding guidelines: src/**/*.rs says, “Don't abbreviate variable names. Use queue not q, message not msg, channel not ch. Common abbreviations like config are fine.”

Also applies to: 215-216

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/settings/store.rs` around lines 196 - 202, Rename the closure bindings in
the new home and pause accessors from abbreviated names to descriptive names:
use target instead of t and value instead of v. Update all references within
those closures while preserving the existing filtering and explicit-value
matching behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design-docs/autonomy.md`:
- Around line 310-311: Update the earlier enrichment pattern at the
`set_outcome` reference to use `autonomy_complete`, matching the completion flow
described near “Calls autonomy_complete.” Keep the surrounding enrichment
sequence unchanged.
- Around line 274-282: Update the earlier cortex wake-context description and
the implementation note around the run-summary handling to remove separate
injection of recent run summaries from the runs table. Keep the transcript,
including persisted assistant-turn summaries, as the sole continuity delivery
path while retaining the run store only for indexing and provenance.

In `@docs/design-docs/home-channel.md`:
- Around line 124-127: Update the wake-target resolution logic in the target
parser around parse_delivery_target and SettingsStore::home_channel() to
distinguish an absent wake target from an explicitly provided but invalid one.
Preserve home-channel fallback only when no target is supplied; treat an
unparseable explicit target as unset for delivery and route it through the
record/silence branch without sending to home.

In `@src/agent/channel.rs`:
- Around line 2308-2309: Verify whether REGISTRY.parse and parse_addressed can
produce different results for the same input, especially around addressing
requirements. If they can, update the handle_message control-command branch
around handle_control_command to check authority with the parsed command
definition using the same context.allows(...) gate as dispatch_inbound before
executing the control action; preserve existing handling for authorized
commands.
- Around line 2622-2653: Update handle_message_batch’s non-suppressed completion
path, alongside its message_count increment and check_memory_persistence call,
to await claim_home_channel_if_unset. Preserve the existing suppression behavior
and ensure batched turns adopt the home channel after their first completed
turn.

In `@src/commands/control.rs`:
- Around line 354-364: The home-channel check in the control flow around
conversation_broadcast_target and SettingsStore::adopt_home_channel is racy
because the read and writes are separate. Move the empty-home validation and
both home-value updates into one serialized storage transaction, ensuring
exactly one concurrent claimant succeeds and subsequent claimants return without
announcing adoption; add a concurrency test covering two simultaneous adoption
attempts.

In `@src/settings/store.rs`:
- Around line 194-210: Update home-channel and pause-state accessors and
mutators, including home_channel(), pause_reason(), adopt_home_channel(),
set_home_channel(), clear_home_channel(), and set_paused(), to distinguish
NotFound from other storage errors and propagate or explicitly log non-NotFound
failures instead of defaulting silently. Perform each coupled state transition
in a single redb write transaction so concurrent claims are serialized and
home/pause fields remain consistent, preserving explicit home-channel values and
fail-safe pause behavior. Add concurrency and storage-failure-injection tests
covering these transitions.

In `@src/tools/set_home_channel.rs`:
- Around line 78-86: Enforce sender authority in the set-home-channel tool flow
before invoking set_home_channel: propagate the verified authority through tool
registration or the tool state used by call, and either omit registration for
unauthorized turns or return a structured authorization error. Ensure
non-authority requests never reach crate::commands::control::set_home_channel.

---

Nitpick comments:
In `@src/settings/store.rs`:
- Around line 196-202: Rename the closure bindings in the new home and pause
accessors from abbreviated names to descriptive names: use target instead of t
and value instead of v. Update all references within those closures while
preserving the existing filtering and explicit-value matching behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2095eb1-ab29-443f-98ab-410562605a45

📥 Commits

Reviewing files that changed from the base of the PR and between c972487 and 845936f.

📒 Files selected for processing (26)
  • docs/design-docs/autonomy.md
  • docs/design-docs/home-channel.md
  • interface/src/api/client.ts
  • interface/src/api/schema.d.ts
  • interface/src/components/autonomy/AutonomyDialCard.tsx
  • interface/src/routes/AgentAutonomy.tsx
  • prompts/en/tools/set_home_channel_description.md.j2
  • src/agent/autonomy.rs
  • src/agent/channel.rs
  • src/api/autonomy.rs
  • src/api/server.rs
  • src/commands/control.rs
  • src/commands/dispatch.rs
  • src/commands/registry.rs
  • src/config/load.rs
  • src/config/toml_schema.rs
  • src/config/types.rs
  • src/cron/scheduler.rs
  • src/lib.rs
  • src/main.rs
  • src/messaging/target.rs
  • src/prompts/text.rs
  • src/settings.rs
  • src/settings/store.rs
  • src/tools.rs
  • src/tools/set_home_channel.rs

Comment thread docs/design-docs/autonomy.md
Comment thread docs/design-docs/autonomy.md
Comment thread docs/design-docs/home-channel.md
Comment thread src/agent/channel.rs Outdated
Comment thread src/agent/channel.rs
Comment thread src/commands/control.rs
Comment on lines +354 to +364
let settings = deps.runtime_config.settings.load().as_ref().clone()?;
if settings.home_channel().is_some() {
return None;
}

let target = conversation_broadcast_target(deps, conversation_id, is_portal)
.await
.ok()?
.to_string();

match settings.adopt_home_channel(&target) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make first-run home adoption atomic.

Two completed user turns can both pass the check on Line 355. SettingsStore::adopt_home_channel also performs a separate read before its writes. The later write can replace the earlier adopted target, and both conversations can announce that they became the home channel.

Perform the empty-home check and both home values writes in one serialized storage transaction. Add a concurrent adoption test that permits exactly one successful claimant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/control.rs` around lines 354 - 364, The home-channel check in
the control flow around conversation_broadcast_target and
SettingsStore::adopt_home_channel is racy because the read and writes are
separate. Move the empty-home validation and both home-value updates into one
serialized storage transaction, ensuring exactly one concurrent claimant
succeeds and subsequent claimants return without announcing adoption; add a
concurrency test covering two simultaneous adoption attempts.

Comment thread src/settings/store.rs
Comment on lines +78 to +86
async fn call(&self, _args: Self::Args) -> Result<Self::Output, Self::Error> {
let message = crate::commands::control::set_home_channel(
&self.deps,
&self.conversation_id,
self.is_portal,
)
.await;

Ok(SetHomeChannelOutput { message })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Enforce authority before changing the home channel.

This tool has no authority check. It bypasses the authority restriction on /sethome. A non-authority sender can use prompt injection to cause the model to set that sender's conversation as the instance home channel.

Pass verified sender authority into the tool registration or tool state. Do not register the tool for non-authority turns, or return a structured authorization error before calling set_home_channel.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/set_home_channel.rs` around lines 78 - 86, Enforce sender authority
in the set-home-channel tool flow before invoking set_home_channel: propagate
the verified authority through tool registration or the tool state used by call,
and either omit registration for unauthorized turns or return a structured
authorization error. Ensure non-authority requests never reach
crate::commands::control::set_home_channel.

jamiepine and others added 4 commits August 10, 2026 02:40
An empty instance surveyed nothing, concluded "nothing new here", and
exited — and since nothing changed, every later wake reached the same
conclusion. The survey already knows when it came back empty, so the
briefing branches on it rather than leaving the agent to notice: read
what is here, record it, check `spacebot_docs` for capability gaps, and
work out the one question worth asking.

Recording is instructed at every level and framed as a licence rather
than a quota, so a run that learned nothing records nothing instead of
manufacturing an observation to fill the space.

`spacebot_docs` was registered only on the branch and cortex tool
servers. The autonomy channel takes direct mode and does not branch, so
it could not reach the docs it is now told to consult.
…omic

Review findings from #635.

The channel re-parses raw text with `REGISTRY.parse`, which the router had
already declined via `parse_addressed`. Those diverge: with a bot username
configured, `/sethome@otherbot` is not a command to the router but resolves
in the channel — running an Authority command with no check. Authority now
resolves once in the router, where the binding config lives, and rides the
message as metadata. Absent means none, so a path that never passed the
router cannot inherit one. Both paths gate through `access_allows`.

The set_home_channel tool had the same hole from the other side: no check
at all, so a non-authority sender could talk the model into pointing the
instance's outreach at their own conversation. It is now registered only on
turns driven by a sender holding authority.

An explicit wake delivery_target that no longer parses is recorded rather
than redirected home. The wake named somewhere that is not home, and a
rename must not turn that into a send to the destination it declined.

Settings state that is only meaningful together now moves in one redb
transaction, and adoption does its empty check inside the write, so two
first-run claimants cannot both win. Reads distinguish a missing key from
an unreadable store: an unreadable pause reports paused, since resuming is
one command away but running through an emergency stop is not recoverable.

Home adoption also fires from the coalesced-batch path, which a fresh
instance's first turns often take.
…nel' into jamiepine/commands-and-home-channel

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/agent/autonomy.rs (1)

557-586: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Apply visibility filtering before the 200-task limit.

TaskStore::list returns at most 200 tasks before task_visible_to_agent filters them. If the first 200 tasks belong to other agents, has_tasks becomes false even when this agent has visible tasks later. The new empty-instance branch can then give cold-start guidance and cause the agent to skip assigned work. Push the visibility predicate into the query or paginate until a visible task is found, and add a multi-agent regression test with more than 200 tasks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent/autonomy.rs` around lines 557 - 586, The render_task_state flow
currently applies task_visible_to_agent after TaskStore::list truncates results
to 200, so visible tasks beyond that limit are missed. Move the visibility
predicate into the task-store query when supported, or paginate through results
until all relevant visible tasks are found, while preserving the existing
section/status behavior; add a regression test covering more than 200 tasks
across multiple agents.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@prompts/en/autonomy_channel.md.j2`:
- Around line 60-71: Update the empty-instance instructions and the following
global memory guidance so they do not direct observe runs to call memory_save.
Gate both recording instructions behind the existing write-permission condition,
preserving read-only behavior for observe runs; do not treat memory_save as an
exception unless that contract is explicitly established and tested.

In `@src/agent/autonomy.rs`:
- Around line 526-529: Update the instance_is_empty predicate in the autonomy
run setup to classify a run as non-empty whenever it has tasks, active goals,
wake_events, or active_workers. Preserve the empty classification only when all
four autonomy signals are absent.

In `@src/commands/dispatch.rs`:
- Around line 89-108: Move the authority resolution and AUTHORITY_METADATA_KEY
stamping out of the post-content branch and before the MessageContent match in
the inbound dispatch flow. Ensure every non-system message, including
MessageContent::Media captions, receives the computed authority metadata before
any content-type return, while preserving the existing AccessContext inputs and
authority calculation.

---

Outside diff comments:
In `@src/agent/autonomy.rs`:
- Around line 557-586: The render_task_state flow currently applies
task_visible_to_agent after TaskStore::list truncates results to 200, so visible
tasks beyond that limit are missed. Move the visibility predicate into the
task-store query when supported, or paginate through results until all relevant
visible tasks are found, while preserving the existing section/status behavior;
add a regression test covering more than 200 tasks across multiple agents.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 951d56cf-3938-4ad0-a9f1-b2ae2cb2b406

📥 Commits

Reviewing files that changed from the base of the PR and between 845936f and 60a343e.

📒 Files selected for processing (12)
  • docs/design-docs/autonomy.md
  • prompts/en/autonomy_channel.md.j2
  • src/agent/autonomy.rs
  • src/agent/channel.rs
  • src/commands.rs
  • src/commands/access.rs
  • src/commands/dispatch.rs
  • src/messaging/target.rs
  • src/prompts/engine.rs
  • src/settings/store.rs
  • src/tools.rs
  • tests/context_dump.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/agent/channel.rs
  • src/messaging/target.rs
  • src/tools.rs
  • src/settings/store.rs
  • docs/design-docs/autonomy.md

Comment on lines +60 to +71
{% if instance_is_empty %}
There are no tasks and no goals. That is not a reason to do nothing — on an instance this empty, learning about {{ agent_name }}'s user and this system is the most valuable work available, and it is what makes every later run better. This run:
- **Look at what is actually here.** The workspace, registered projects, files the user has already put in place. A cloned repository is a statement of intent — read it and work out what they are trying to do.
- **Record what you learn** as memories, following the rule below.
- **Find the gaps.** Use `spacebot_docs` to check which capabilities fit what this user appears to be doing and have not been set up yet.
- **Work out the one question worth asking.** If there is a single thing the user could tell you that would unlock the most, identify it and put it in your summary. One answered question is worth more than a long survey of an empty system.

Do not invent tasks to look busy. Proposing work nobody asked for is worse than proposing nothing.
{% endif %}

Record what you notice as you go, with `memory_save`, rather than saving it all up for the end — a run can be cut short by its timeout and lose everything it was holding. Save what would genuinely be useful to a future run or to the user: what this system is for, how they work, what they care about, what is broken. This is a licence, not a quota. A run that learned nothing records nothing, and that is a perfectly good run — do not restate what this briefing already told you, and do not record your own activity as though it were a discovery.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep observe runs read-only.

The observe rules at Line 45 prohibit all mutations, but the empty-instance block and the global guidance tell every level to record findings with memory_save. This can persist data during an observe run. Gate both instructions to levels that permit writes, or explicitly define memory_save as an observe exception and test that contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prompts/en/autonomy_channel.md.j2` around lines 60 - 71, Update the
empty-instance instructions and the following global memory guidance so they do
not direct observe runs to call memory_save. Gate both recording instructions
behind the existing write-permission condition, preserving read-only behavior
for observe runs; do not treat memory_save as an exception unless that contract
is explicitly established and tested.

Comment thread src/agent/autonomy.rs
Comment thread src/commands/dispatch.rs Outdated
…ld start

Second round of review findings from #635.

Authority was stamped after the content match, which returns early for
media. The channel builds its command text from captions and registers
authority-gated tools per turn, so an authority sender lost both on any
turn carrying a file. Resolution and stamping now happen ahead of the
match, in one function the test drives directly.

A run woken by a wake event, or with workers still running, is no longer
classified as an empty instance. It has a reason to exist and a bounded
turn to spend on it; cold-start discovery would spend that on the
workspace instead.

The observe rules read as forbidding memory writes while the guidance
below them asked for memory writes. Recording is the point of the level —
observe accumulates memories and says nothing — so the exception is now
explicit rather than the instruction being gated.
…nel' into jamiepine/commands-and-home-channel
@jamiepine
jamiepine merged commit 7d92577 into main Aug 10, 2026
5 checks passed
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