Skip to content

feat(v1): add Harness.transform_prompt hook#1561

Open
hubert-marek wants to merge 3 commits into
mainfrom
feat/harness-transform-prompt-hook
Open

feat(v1): add Harness.transform_prompt hook#1561
hubert-marek wants to merge 3 commits into
mainfrom
feat/harness-transform-prompt-hook

Conversation

@hubert-marek

@hubert-marek hubert-marek commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Adds a default-identity Harness.transform_prompt(prompt, state) hook, applied in Runtime.submit_model_request right before the model call — the single point every model request passes through (base-program, fn-mode, host-loop).

Subclasses override it to reshape the prompt before each request (e.g. context compaction, redaction, reminders); the returned messages are what the host tokenizes, sends, and records as the trajectory step. Default is identity, so existing harnesses are unaffected. Sync and async both supported.

2 files, 28 insertions.

🤖 Generated with Claude Code


Note

Medium Risk
The hook runs on every model request and can change what is sent and logged; incorrect overrides could break rollouts or trajectories, though the default identity path leaves existing harnesses unchanged.

Overview
Adds a Harness.prepare_prompt(prompt, state) hook (default: no change) so subclasses can reshape messages immediately before each model call—e.g. compaction, redaction, or injected reminders.

Runtime.submit_model_request is the single choke point: it runs the hook (sync or async) before tokenization and the client request, and the trajectory step stores the prepared prompt. That applies uniformly to base-program, fn-mode, and host-loop rollouts.

The built-in base program loop now re-reads the last trajectory step’s prompt after each turn so its in-memory messages stay aligned with what was actually sent when the hook modifies the prompt.

docs/byo-harness.md documents the hook with an override example.

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

Note

Add Harness.transform_prompt hook to reshape messages before each model request

  • Adds a prepare_prompt(prompt, state) method to Harness that is an identity transform by default and intended to be overridden by subclasses.
  • Runtime.submit_model_request now calls an internal _prepare_prompt helper before issuing the model request, supporting both sync and async hook implementations.
  • The host loop in Harness realigns its working messages variable to the prepared prompt after each model request, so subsequent turns build on exactly what was sent to the model.
  • Documents the hook in docs/byo-harness.md with an example subclass that prepends a reminder to the first system message.
  • Behavioral Change: any harness subclass that overrides prepare_prompt will now alter what the model sees and what is recorded in the trajectory.
📊 Macroscope summarized dadff8e. 3 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

Add an overridable, default-identity Harness.transform_prompt(prompt, state),
applied in Runtime.submit_model_request immediately before the model call — the
single point every model request passes through (base-program, fn-mode, and
host-loop). Subclasses override it to return a modified prompt; the result is
what the host tokenizes, sends to the model, and records as the trajectory step.
Default is identity, so existing harnesses are unaffected. Sync and async
overrides are both supported.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hubert-marek hubert-marek requested a review from willccbb June 8, 2026 00:58
Comment thread verifiers/v1/harness.py
Comment thread verifiers/v1/harness.py
@macroscopeapp

macroscopeapp Bot commented Jun 8, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR introduces a new feature (prepare_prompt hook) that modifies the core model request flow. Additionally, an open review comment identifies a medium-severity issue where sandbox mode may diverge from the prepared prompt, warranting human review.

You can customize Macroscope's approvability policy. Learn more.

@willccbb

willccbb commented Jun 8, 2026

Copy link
Copy Markdown
Member

@cursoragent change function name to "prepare_prompt", ensure it's triggered for base program (see comment), add to byo-harness documentation

@cursor

cursor Bot commented Jun 8, 2026

Copy link
Copy Markdown
  • Renamed the hook to prepare_prompt and wired it through the runtime. Updated calls and internals:

    • verifiers/v1/harness.py: added Harness.prepare_prompt(prompt, state) (identity by default).
    • verifiers/v1/runtime.py: now applies harness.prepare_prompt via _prepare_prompt inside submit_model_request before every model call.
  • Ensured it’s triggered for the base program and that the loop uses the prepared prompt:

    • verifiers/v1/harness.py: in base_program, after each submit_model_request, the loop reloads the exact prepared prompt from the latest trajectory step so subsequent turns, sync_completion, and transcripts stay consistent.
  • Added BYO-harness documentation for the new hook:

    • docs/byo-harness.md: new “Prompt Preparation Hook” section explaining prepare_prompt with a minimal example.

I ran ruff via uvx (lint clean), pushed the updates to feat/harness-transform-prompt-hook, and updated the existing PR.

Open in Web Open in Cursor 

cursoragent and others added 2 commits June 8, 2026 01:35
… in Runtime.submit_model_request

Co-authored-by: will brown <willccbb@users.noreply.github.com>
Co-authored-by: will brown <willccbb@users.noreply.github.com>
Comment thread docs/byo-harness.md
class MyHarness(vf.Harness[MyHarnessConfig]):
def prepare_prompt(self, prompt: vf.Messages, state: vf.State) -> vf.Messages:
# Example: prepend a brief reminder to the first system message
messages = vf.normalize_messages(prompt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Low docs/byo-harness.md:440

The documentation example on line 440 calls vf.normalize_messages(prompt), but normalize_messages is not exported from the verifiers.v1 package. Users following this example will hit AttributeError: module 'verifiers.v1' has no attribute 'normalize_messages'. The function exists in verifiers.utils.message_utils but is not re-exported through the vf namespace. Either re-export normalize_messages from the vf package or update the example to import from verifiers.utils.message_utils directly.

-        messages = vf.normalize_messages(prompt)
+        messages = vf.utils.normalize_messages(prompt)
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @docs/byo-harness.md around line 440:

The documentation example on line 440 calls `vf.normalize_messages(prompt)`, but `normalize_messages` is not exported from the `verifiers.v1` package. Users following this example will hit `AttributeError: module 'verifiers.v1' has no attribute 'normalize_messages'`. The function exists in `verifiers.utils.message_utils` but is not re-exported through the `vf` namespace. Either re-export `normalize_messages` from the `vf` package or update the example to import from `verifiers.utils.message_utils` directly.

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dadff8e. Configure here.

Comment thread verifiers/v1/runtime.py
result = harness.prepare_prompt(prompt, state)
if inspect.isawaitable(result):
result = await result
return result if result is not None else prompt

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sandbox base ignores prepared prompt

Medium Severity

prepare_prompt runs on every submit_model_request, including sandbox base-program /vf/model calls, but only the in-process base_program loop re-reads the prepared prompt from the trajectory afterward. The sandbox runner keeps extending its local messages with the raw thread, so overrides that reshape history diverge from what the model saw and what call_user receives.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit dadff8e. Configure here.

Comment thread docs/byo-harness.md
content = str(messages[0].content or "")
messages[0].content = "Answer concisely. " + content
return messages
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing reference documentation entry

Low Severity

This PR adds the user-facing Harness.prepare_prompt hook but only documents it in byo-harness.md. Core harness customization points are also enumerated in docs/reference.md (alongside load_system_prompt); that reference list was not updated for the new hook.

Fix in Cursor Fix in Web

Triggered by project rule: BugBot Instructions

Reviewed by Cursor Bugbot for commit dadff8e. Configure here.

Comment thread verifiers/v1/harness.py
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.

3 participants