Skip to content

fix(openclaw): capture prompts under the run's project binding - #2601

Open
Fengzdadi wants to merge 2 commits into
oceanbase:masterfrom
Fengzdadi:fix/openclaw-capture-project-binding
Open

Fengzdadi wants to merge 2 commits into
oceanbase:masterfrom
Fengzdadi:fix/openclaw-capture-project-binding

Conversation

@Fengzdadi

@Fengzdadi Fengzdadi commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

No tracking issue. A real-host OpenClaw service-chain E2E, which will follow in its own PR, found this defect.

Rationale for this change

The OpenClaw integration docs say the plugin resolves one existing Scope before every operation. Resolution uses an explicit scopeId, then durable bindings for the OpenClaw session, the ordered active projects, and the agent identity. Automatic capture did not apply the project step.

OpenClaw 2026.8.1 passes the run's activeProjectKeys to before_prompt_build but not to agent_end; buildEmbeddedAgentEndContext omits them. The capture's /v1/scope-bindings/resolve request therefore carried only the session and agent keys.

Suppose a project binding exists but no session binding. The turn's recall reads the project Scope, while its prompt is captured into the agent or default Scope. Prompts captured inside a project are then never recalled in that project.

What changes are included in this PR?

  • Run-scoped project keys. before_prompt_build records the run's active project keys by runId. It does this before the autoRecall check, so capture still works with recall turned off.
  • Capture uses them. agent_end reads the record for its run and uses it when the host omits activeProjectKeys. Keys the host supplies to agent_end itself still take precedence.
  • Replay-safe, bounded memory. At agent_end, whether or not the turn succeeded, the record moves from the runs in progress to the completed runs. A replayed agent_end, including a retry after a failed capture, then resolves the same Scope; Source deduplication holds only within one Scope, so resolving a replay elsewhere would store the prompt twice. Each map keeps its 64 most recent runs, so completed runs never evict a run in progress.
  • Unit test. A regression test in lifecycle.test.ts runs turns whose agent_end context carries the run but not its projects. It checks capture with recall on and with recall off, and that host-supplied keys win. Two more tests replay a turn's agent_end: after a successful capture, both deliveries go to the project Scope with the same Source, and closing flushes only that Scope; after a failed capture, the retry also goes to the project Scope.

Are there any user-facing changes?

Yes, a fix. With a project binding, automatically captured prompts now land in the project's Scope, the same Scope recall reads. No configuration, API, or docs change.

How was this change tested?

  • make openclaw-plugin-test: 103 tests pass, plus type check and build.
  • The new unit test fails without the fix (captures land in scp_default). It also fails if the precedence is reversed. Both replay tests fail when agent_end removes the record, because the replay goes to scp_default.
  • A throwaway check, not committed because it would pin the 64-run bound, kept a long run's project binding while 70 other runs started and finished. It fails when completed runs stay in the in-progress map.
  • make check passes.
  • Real host: OpenClaw 2026.8.1 Gateway (openclaw gateway run) with the plugin linked, a real Server, and a deterministic model fixture.
    • The upcoming E2E's Scope case binds the agent, then the workspace project, then the session, and checks where each turn's prompt is captured.
    • It fails on master, where the project-bound turn's prompt goes to the agent Scope.
    • It passes with this change. The other five E2E cases also pass.

Not validated:

  • Prompt injection disabled. If an operator sets hooks.allowPromptInjection: false, OpenClaw does not run before_prompt_build, so capture falls back to the agent and session bindings.
  • Pre-compaction capture. before_compaction is unchanged. OpenClaw 2026.8.1 does not pass project keys to it either. The built-in runner's auto-compaction call passes only sessionKey, with no agentId, so the plugin's pre-compaction checkpoint returns early on that path. That is a separate defect.
  • Other OpenClaw releases were not run.
  • Replay on a real host. The replay tests use the unit harness; the real-host run above did not replay agent_end.

A high-effort self-review of the diff found five items:

  • Fixed: the test now also covers host-supplied keys taking precedence.
  • No change needed: plugin tools already receive activeProjectKeys from OpenClaw.
  • Left as limitations above: prompt injection disabled, and before_compaction.
  • Accepted by design: with more than 64 runs in flight at once, the oldest run's record is dropped.

A high-effort review of the replay handling found four items:

  • Fixed: completed runs are kept apart from runs in progress, so they never evict one.
  • Fixed: a test covers the retry after a failed capture.
  • Fixed: the lookup and the move to the completed runs are one helper, completeRunProjects.
  • Accepted: if a host reused a run id, a run without project keys would resolve the projects of the latest completed run with that id.

AI usage statement

Claude Code (Claude Opus 5.5) investigated the OpenClaw host source, wrote the change and the tests, and ran the validation above.

OpenClaw passes a run's activeProjectKeys to before_prompt_build but not
to agent_end, so automatic capture resolved its Scope without the project
binding that the same run's recall used. Record the run's project keys at
before_prompt_build and use them at agent_end when the host omits them.
Copilot AI balanced review requested due to automatic review settings November 12, 2026 05:25

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

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

Reviewed a86fc78. No blocking findings. Carrying the run's project keys into capture fixes the recall/capture Scope mismatch while preserving host-supplied keys and the existing Server resolution order. No new database tables or public interfaces. The regression reproduces against the base implementation and passes on this head; interleaved-run and cleanup probes also pass. Real OpenClaw Gateway + Server acceptance was not independently rerun.

return undefined;
}
const projectKeys = runProjectKeys.get(runId);
runProjectKeys.delete(runId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Preserve project bindings across completion replays

Replaying a successful agent_end without project keys—a case already covered by the lifecycle's replay contract—now resolves to the agent/default Scope because the first delivery deletes the saved binding. Since Source deduplication is Scope-local, this duplicates the project prompt into another Scope; preserve replay-safe binding resolution or suppress already captured runs while keeping memory bounded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 5a91c50. agent_end no longer drops the run's projects: it moves them to a separate map of completed runs, so a replayed agent_end, including a retry after a failed capture, resolves the same project Scope. Both maps keep their 64 most recent runs, so completed runs never evict runs still in progress.

captures a replayed turn under the same project binding and retries a failed capture under the same project binding when the turn is replayed cover the two cases. Both fail at a86fc780, where the replay goes to scp_default.

The first `agent_end` removed the run's saved projects, so a replayed
`agent_end` captured into the agent or default Scope. Source
deduplication holds only within one Scope, so the prompt was stored
twice. A completed run now moves to its own bounded map: a replay, or
a retry after a failed capture, resolves the same project Scope, and
completed runs do not evict runs that are still in progress.
Copilot AI balanced review requested due to automatic review settings November 13, 2026 06:10

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

This branch has not been deployed

No deployments
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.

4 participants