Skip to content

fix(triggers): add unattended scheduled-run protocol - #7495

Closed
serrrfirat wants to merge 1 commit into
nearai:mainfrom
serrrfirat:codex/scheduled-trigger-unattended-prompt
Closed

serrrfirat wants to merge 1 commit into
nearai:mainfrom
serrrfirat:codex/scheduled-trigger-unattended-prompt

Conversation

@serrrfirat

Copy link
Copy Markdown
Collaborator

Summary

  • append a loop-owned unattended-run protocol when the trusted turn origin is ScheduledTrigger
  • tell scheduled runs to execute with bounded assumptions, produce a self-contained recorded result, and preserve all host gates
  • keep interactive prompt behavior unchanged and document the trigger/prompt ownership contract

Change Type

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • CI/Infrastructure
  • Security
  • Dependencies

Linked Issue

Related #6879

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all --benches --tests --examples --all-features -- -D warnings — targeted owning-crate clippy was run instead
  • cargo build — covered by the targeted test and clippy builds below
  • Relevant tests pass: composition prompt regression, loop-host prompt assets, triggered-submit integration, composition prompt boundary
  • cargo test -p <owning-crate> --features integration — Not applicable: no database-backed owning-crate integration feature changed
  • Manual testing — Not applicable: behavior is deterministic prompt assembly and is covered at the identity-source caller seam
  • If a coding agent was used and supports it, review-pr or pr-shepherd --fix was run before requesting review — not run

Test Strategy

User behavior: Scheduled routines are explicitly told that no human is present, must perform the stored task instead of ending with a question, and must produce a self-contained recorded result. Interactive runs retain their existing ask-the-user behavior.

Risk areas:

  • Model behavior
  • Browser
  • Side effect
  • Persistence
  • Security or permissions
  • External provider
  • Cross-component behavior

Tests added or updated:

  • Unit or contract: Added scheduled_trigger_origin_appends_unattended_protocol_only_to_triggered_runs; extended prompt-asset guards for the new Markdown asset.
  • Reborn integration: Existing reborn_integration_triggered_submit suite passes (18 tests). The harness intentionally wires EmptyIdentityContextSource, so the new prompt assertion lives at the real HostIdentityContextSource caller seam instead of being vacuous in this suite.
  • Recorded fixture: Not applicable: no provider-dependent model decision is needed to verify origin-based prompt assembly.
  • Browser E2E: Not applicable: no browser surface changed.
  • Backend or runtime: Not applicable: no persistence backend, runtime lane, or configuration shape changed.
  • Live canary: Not applicable: deterministic prompt selection is fully covered hermetically.

What the tests prove: A trusted ScheduledTrigger context receives the unattended protocol, an explicit Inbound context does not, the asset remains valid and distinct, existing triggered submission behavior remains green, and prompt text stays outside the composition crate.

Commands run:

  • cargo fmt --all -- --check
  • cargo test -p ironclaw_composition scheduled_trigger_origin_appends_unattended_protocol_only_to_triggered_runs --lib
  • cargo test -p ironclaw_loop_host system_prompt_assets --lib
  • cargo test -p ironclaw_integration_tests --test reborn_integration_triggered_submit
  • cargo test -p ironclaw_architecture_tests --test reborn_composition_boundaries composition_root_embeds_no_prompt_content
  • cargo clippy -p ironclaw_loop_host -p ironclaw_composition --lib --tests -- -D warnings
  • python3 scripts/ci/docs_publication_boundary.py

Security Impact

None. This does not change authorization, approvals, authentication, permissions, secrets, tool execution, or delivery routing. The protocol explicitly preserves existing host gates and forbids guessing credentials, permissions, or destinations.

Reborn Trust-Boundary Checklist

  • Public policy/evidence/trust-bearing types: Not applicable; no types or constructors changed.
  • Untrusted content enters prompts only through an envelope/escaping primitive: Not applicable; the new text is a host-owned static prompt asset and user content handling is unchanged.
  • Hashes declare purpose; trust/binding/authenticity uses SHA-256/BLAKE3 or separate authenticity check: Not applicable; no hashes changed.
  • New/changed status, exit, policy, runtime, or error variants: Not applicable; no variants changed.
  • Security/durability serde(default) fields fail closed or have migration tests: Not applicable; no serialized fields changed.
  • Queues/maps/buffers/counters have bounds and overflow-safe arithmetic: Not applicable; none changed.
  • Driver/operator-visible errors have stable class semantics: Not applicable; no errors changed.
  • Sandbox/native/host names accurately describe trust boundary: ScheduledTrigger remains the existing trusted-origin discriminator; no runtime lane names changed.

Database Impact

None. No schema, migration, query, or backend behavior changes.

Blast Radius

Limited to default system-prompt assembly for runs whose trusted product origin is ScheduledTrigger, plus the loop-host prompt asset registry and trigger contract documentation. Interactive, inbound-channel, and WebUI turns do not receive the new protocol.

Rollback Plan

Revert this commit. There is no migration or persisted-state compatibility concern; removing the conditional append restores the previous prompt behavior immediately.

Review Follow-Through

A [SILENT]-style suppression sentinel is intentionally not included in this slice because no deterministic delivery-layer consumer exists yet; prompt-only support could deliver the literal marker. That should land with explicit suppression semantics in a follow-up.


Review track: B

@github-actions github-actions Bot added scope: docs Documentation size: M 50-199 changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Aug 11, 2026
@ironloopai

ironloopai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

🧭 IronLoop Run · Review

This comment updates in place as the Run moves through its stages.

🟩 Final result · Completed

🟨 Queued → 🟦 Working → 🟦 Posting results → 🟩 Completed

Automatic trigger · attempt 1 of 3 · completed in 14m 54s

IronLoop completed the review and posted it to GitHub.

🔗 Result

Open submitted review →

Run details

Run: 60abf5ab-4529-42c0-8222-380ce433e1ed
Base: main at 2938f24
Head: codex/scheduled-trigger-unattended-prompt at deab24f
Created: 2026-08-11 09:32 UTC
Updated: 2026-08-11 09:47 UTC

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added dedicated guidance for unattended scheduled runs.
    • Scheduled tasks now execute with bounded assumptions and provide self-contained results without requesting clarification.
    • Approval, authentication, authorization, and policy safeguards remain enforced.
    • Interactive runs continue using the standard prompt behavior.
  • Documentation

    • Documented scheduled-trigger behavior and prompt assembly.

Walkthrough

Changes

Scheduled-trigger prompt behavior

Layer / File(s) Summary
Prompt asset contract
crates/loop/ironclaw_loop_host/prompts/scheduled_trigger_mode.md, crates/loop/ironclaw_loop_host/src/system_prompt_assets.rs, crates/loop/ironclaw_loop_host/src/lib.rs, crates/loop/ironclaw_loop_host/AGENTS.md, docs/reborn/target-architecture/families/loop.md
Adds and exports the scheduled-trigger protocol. Asset tests validate its content, heading, and distinctness. Ownership documentation identifies the loop host asset and composition-root assembly.
Conditional prompt assembly and coverage
crates/app/ironclaw_composition/src/root/default_system_prompt.rs, docs/reborn/contracts/triggers.md
Passes LoopRunContext into prompt generation and appends the protocol only for ScheduledTrigger origins. Tests verify scheduled and interactive prompt behavior. Trigger documentation records the same condition.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • nearai/ironclaw#7131: Related scheduled-trigger execution and trigger contract changes, focused on failure settlement and delivery.
  • nearai/ironclaw#7157: Related scheduled-trigger origin handling and trigger prompt behavior.

Suggested reviewers: benkurrek

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits style and clearly describes the unattended scheduled-run protocol change.
Description check ✅ Passed The description covers the required sections, change scope, validation, testing, security, database impact, rollback, and review follow-through.
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.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@crates/app/ironclaw_composition/src/root/default_system_prompt.rs`:
- Around line 558-604: The test
scheduled_trigger_origin_appends_unattended_protocol_only_to_triggered_runs
currently covers only Inbound as the non-scheduled case. Add a
TurnOriginKind::WebUi context, resolve its prompt content through the same
helper, and assert it does not contain the unattended scheduled-run instructions
while preserving the existing Inbound and ScheduledTrigger assertions.

In `@crates/loop/ironclaw_loop_host/prompts/scheduled_trigger_mode.md`:
- Around line 5-6: Update the scheduled trigger instructions near “When details
are ambiguous” to define the unsafe-ambiguity path: stop execution, report the
specific missing input, and record a self-contained result without asking a
question or inventing a value. Preserve the existing requirement to make bounded
assumptions when they are safe.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a44ea44-f1ba-46e2-bd93-8d842ca4fb3f

📥 Commits

Reviewing files that changed from the base of the PR and between 2938f24 and deab24f.

📒 Files selected for processing (7)
  • crates/app/ironclaw_composition/src/root/default_system_prompt.rs
  • crates/loop/ironclaw_loop_host/AGENTS.md
  • crates/loop/ironclaw_loop_host/prompts/scheduled_trigger_mode.md
  • crates/loop/ironclaw_loop_host/src/lib.rs
  • crates/loop/ironclaw_loop_host/src/system_prompt_assets.rs
  • docs/reborn/contracts/triggers.md
  • docs/reborn/target-architecture/families/loop.md

Comment on lines +558 to +604
#[tokio::test]
async fn scheduled_trigger_origin_appends_unattended_protocol_only_to_triggered_runs() {
let root = tempfile::tempdir().expect("tempdir");
let storage_root = root.path().canonicalize().expect("canonical root");
let prompt_path = storage_root.join("system/prompts/default-system.md");
seed_default_system_prompt(&storage_root, &prompt_path).expect("prompt seeds");
let source =
DefaultSystemPromptIdentitySource::try_new(storage_root, prompt_path, false, false)
.expect("prompt loads");
let interactive_context = run_context_with_origin(TurnOriginKind::Inbound).await;
let scheduled_context = run_context_with_origin(TurnOriginKind::ScheduledTrigger).await;

async fn resolve_content(
source: &DefaultSystemPromptIdentitySource,
context: &LoopRunContext,
) -> String {
let candidates = source
.load_identity_candidates(context, PromptMode::TextOnly)
.await
.expect("candidates load");
source
.resolve_identity_message_content(
context,
candidates[0]
.message_ref
.as_ref()
.expect("trusted identity has ref"),
)
.await
.expect("resolve content")
.expect("content exists")
.content
}

let interactive_content = resolve_content(&source, &interactive_context).await;
let scheduled_content = resolve_content(&source, &scheduled_context).await;

assert!(
!interactive_content.contains("Unattended Scheduled Run"),
"interactive runs must retain the ordinary ask-the-user escape valve"
);
assert!(scheduled_content.contains("Unattended Scheduled Run"));
assert!(scheduled_content.contains("There is no human present"));
assert!(scheduled_content.contains("Never end the run with a question"));
assert!(scheduled_content.contains("final reply is the run's recorded output"));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the TurnOriginKind::WebUi negative path.

The PR promises unchanged WebUI prompt behavior, but this test checks only TurnOriginKind::Inbound. Add TurnOriginKind::WebUi to the same assertion matrix so all non-scheduled product origins are covered.

The PR objective states that interactive, inbound-channel, and WebUI prompt behavior remains unchanged.

🤖 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 `@crates/app/ironclaw_composition/src/root/default_system_prompt.rs` around
lines 558 - 604, The test
scheduled_trigger_origin_appends_unattended_protocol_only_to_triggered_runs
currently covers only Inbound as the non-scheduled case. Add a
TurnOriginKind::WebUi context, resolve its prompt content through the same
helper, and assert it does not contain the unattended scheduled-run instructions
while preserving the existing Inbound and ScheduledTrigger assertions.

Comment on lines +5 to +6
Never end the run with a question, a menu of options, or a description of what you could do next. Use the capabilities available to perform the task now. When details are ambiguous, make reasonable, bounded assumptions that stay within the stored request and state material assumptions briefly in the final reply.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Define the failure path when no bounded assumption is safe.

Line 5 requires a bounded assumption for ambiguity, but it does not define what to do when no safe assumption exists. Add a rule to stop, report the missing input, and avoid a question or fabricated value.

The trigger contract requires bounded assumptions and a self-contained recorded result.

🤖 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 `@crates/loop/ironclaw_loop_host/prompts/scheduled_trigger_mode.md` around
lines 5 - 6, Update the scheduled trigger instructions near “When details are
ambiguous” to define the unsafe-ambiguity path: stop execution, report the
specific missing input, and record a self-contained result without asking a
question or inventing a value. Preserve the existing requirement to make bounded
assumptions when they are safe.

@ironloopai ironloopai 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.

🔍 IronLoop review

One medium-severity regression found in scheduled prompt assembly.

Findings: 🟠 Medium 1

🟠 Medium · Keep the scheduled directive within the identity budget

Inline on crates/app/ironclaw_composition/src/root/default_system_prompt.rs:100. See the inline comment for details.

Validation

  • ✅ Scheduled-origin prompt unit test — The added scheduled-trigger prompt selection test passed.
  • ✅ Identity-budget unit test — The loop-host test confirming that an over-budget trusted identity candidate is omitted passed.
Review details
  • Run: 60abf5ab-4529-42c0-8222-380ce433e1ed
  • Workflow: Review
  • Attempts: 1

.map(|context| context.origin),
Some(TurnOriginKind::ScheduledTrigger)
) {
append_section(&mut content, SCHEDULED_TRIGGER_MODE_PROTOCOL_PROMPT);

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.

🔍 IronLoop review · Inline finding

🟠 Medium · Keep the scheduled directive within the identity budget

This adds the protocol to the same single identity candidate as the user-editable `SYSTEM.md`. The normal identity budget is 8,000 tokens, and an over-budget first candidate is omitted entirely. A valid custom prompt is allowed to be up to 64 KiB; even a roughly 31.5 KiB prompt that previously fit can be pushed over the limit by this new section, causing scheduled runs to receive no system prompt at all—including the unattended directive. Reserve/admit this protocol independently (or enforce a compatible prompt-size limit) and add a boundary regression test.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7497 August 11, 2026 09:54 Destroyed
@serrrfirat

Copy link
Copy Markdown
Collaborator Author

Superseded by #7497, which uses the upstream nearai/ironclaw branch as the PR head.

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-7497 — deab24ff Deployed Aug 11, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules scope: docs Documentation size: M 50-199 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant