Skip to content

fix: address autoresearch review follow-ups - #45

Merged
undivisible merged 1 commit into
mainfrom
fix/autoresearch-review-followups
Aug 7, 2026
Merged

fix: address autoresearch review follow-ups#45
undivisible merged 1 commit into
mainfrom
fix/autoresearch-review-followups

Conversation

@undivisible

@undivisible undivisible commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to the merged autoresearch PR #44, addressing the second review pass.

Implemented:

  • exclude USER.md and MEMORY.md from restricted automation prompts
  • reject baseline/evaluation commands that move tracked state
  • enforce wall-clock process-group termination
  • bound ignored-state snapshots and skip volatile dependency/build roots
  • atomically replace the ledger on Windows
  • normalize paths before workspace containment checks
  • recursively restore submodules on rollback
  • scrub child environments and disable repository hooks for controller commits
  • report unknown model pricing as incomplete instead of silently free

Validation:

  • cargo fmt --all -- --check
  • cargo check -p apollo-agent --lib
  • cargo test -p apollo-agent autoresearch --lib -- --nocapture (12 passed)
  • prompt tests (2 passed), cost tests (6 passed), HTTP tests (10 passed)
  • cargo clippy -p apollo-agent --lib --all-features -- -D warnings

The full workspace test/release sweep was not run because AGENTS.md warns that building apollo-ui can exhaust the machine disk; the targeted checks are green.


Open workspace in Conductor


Note

Medium Risk
Changes sit on git rollback, subprocess execution, and automation prompts for autoresearch—important safety boundaries—but behavior is mostly tightening guards and telemetry rather than new ambient capabilities.

Overview
Autoresearch gets a second safety pass: baseline and per-iteration validation/metric runs must leave branch, HEAD, and tracked git status unchanged (failures restore baseline ignored state); shell/git children inherit scrubbed env, share the run wall-clock budget with process-group kills on timeout, and acceptance commits use --no-verify plus an empty hooks path. Ignored-file rollback moves from in-memory bytes to temp-dir copies with per-file and total snapshot caps while skipping volatile roots like target/node_modules; rollbacks also reset submodules, ledger writes atomically replace on Windows, and in-workspace ledger paths normalize before containment checks.

Restricted automation builds a slimmer system prompt (no USER.md / MEMORY.md) via build_restricted_system_prompt.

Cost reporting marks calls without configured model prices as unpriced (pricing_known / pricing_complete, counts and model lists) instead of treating them as $0; HTTP /v1/state, Telegram cost output, and the usage tool expose when totals are incomplete. Docs describe the new autoresearch guarantees and limits.

Reviewed by Cursor Bugbot for commit e837faa. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_354a2e77-e781-48e4-85f3-7cc30adcff62)

@undivisible
undivisible merged commit d8808a3 into main Aug 7, 2026
3 of 4 checks passed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e837faaf6d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/autoresearch.rs
Comment on lines +439 to +440
ensure_status_unchanged(&self.workspace, &candidate_status, "validation or metric")
.await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Compare candidate contents instead of status entries

When the agent has already modified or created a path, a validation or metric command can rewrite that same path while git status --porcelain remains identical (for example, both snapshots are still M src/foo.rs or ?? new.rs). Git's short-format documentation defines these entries as status codes plus paths, not content snapshots, so this check passes and an accepted experiment can commit changes made by the measurement command itself, invalidating the result. Compare content/index state, such as hashes or diffs, instead.

Useful? React with 👍 / 👎.

Comment thread src/autoresearch.rs
Comment on lines +423 to +424
match evaluation {
Err(error) if is_budget_error(&error) => return Err(error),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore the iteration before propagating evaluation errors

When the finite wall-clock budget expires during validation or measurement, this direct return bypasses both restore_checkpoint and restore_ignored_state; the later ensure_status_unchanged(...)? has the same problem when a command changes the status. The run therefore exits with the agent's candidate and command side effects still in the workspace, and a subsequent --resume immediately fails the clean-workspace precondition. Route these error exits through the same cleanup path used for rejected iterations and cover every such exit with a failing cleanup case.

AGENTS.md reference: AGENTS.md:L526-L528

Useful? React with 👍 / 👎.

Comment thread src/autoresearch.rs
Comment on lines +899 to +900
ensure_experiment_state(workspace, branch, commit).await?;
restore_checkpoint(workspace, commit).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make baseline state violations restorable

If a baseline validation or metric command checks out another branch or moves HEAD, ensure_tracked_state_unchanged correctly rejects it, but the cleanup path calls this same state check again before performing any reset. That check necessarily fails for exactly these violations, so restore_checkpoint is never reached and the workspace remains on the command-selected branch or commit despite the documented baseline restoration guarantee.

Useful? React with 👍 / 👎.

Comment thread src/autoresearch.rs
"apollo-autoresearch-ignored-{}",
uuid::Uuid::new_v4()
));
tokio::fs::create_dir_all(&backup_dir).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Create ignored-state backups with private permissions

On a multi-user Unix host, create_dir_all creates this directory under the shared system temp directory using the process umask, commonly making it searchable by other users. The snapshot explicitly contains ignored local credentials, and copy preserves modes such as a typical 0644 .env; a private workspace can therefore have its secrets exposed through /tmp/apollo-autoresearch-ignored-*, with the directory also surviving a crash because cleanup relies on Drop. Create the backup root atomically with owner-only permissions and constrain backup-file permissions as well.

AGENTS.md reference: AGENTS.md:L110-L114

Useful? React with 👍 / 👎.

Comment thread src/autoresearch.rs
Comment on lines +557 to +560
impl Drop for IgnoredWorkspaceState {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.backup_dir);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove snapshot backups without blocking the Tokio runtime

When each snapshot is dropped, this synchronous recursive filesystem operation runs directly on the async runtime thread. A snapshot may contain 64 MiB and has no file-count bound, so a workspace with many small ignored files can stall the runtime for a substantial period at every iteration boundary. Perform cleanup through Tokio before dropping the state, or delegate the unavoidable synchronous cleanup to spawn_blocking.

AGENTS.md reference: AGENTS.md:L97-L99

Useful? React with 👍 / 👎.

Comment thread src/autoresearch.rs
Comment on lines +591 to +592
":(exclude)vendor",
":(exclude)vendor/**",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore ignored vendor trees between iterations

When an ignored vendor/ tree supplies dependencies to the build, excluding it from both capture and restoration lets an agent or validation command modify dependency source permanently across rejected iterations. If the same iteration also changes a tracked file, the metric can be accepted and recorded against a vendor mutation that is absent from the acceptance commit, so the ledger's supposedly reproducible best commit does not reproduce its metric. Either snapshot these inputs or make cleanup rebuild/remove them before each measurement.

Useful? React with 👍 / 👎.

Comment thread src/autoresearch.rs
) -> anyhow::Result<String> {
let mut command = tokio::process::Command::new("git");
command.args(args).current_dir(workspace);
crate::tools::child_proc::scrub(&mut command);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle Git config environment tuples atomically

In CI or wrappers that configure Git through GIT_CONFIG_COUNT, GIT_CONFIG_KEY_0, and GIT_CONFIG_VALUE_0, the generic scrubber removes GIT_CONFIG_KEY_0 because its name contains KEY but preserves the count and value. Git requires every configured entry to have both fields, as described in its environment configuration documentation, and exits with missing config key GIT_CONFIG_KEY_0; consequently autoresearch fails on its first Git command in these environments. Preserve the complete tuple or remove the count, keys, and values together.

Useful? React with 👍 / 👎.

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