Skip to content

fix(agents): stabilize prompt cache fingerprints#60731

Merged
vincentkoc merged 5 commits into
mainfrom
codex/prompt-fingerprint-stability
Apr 4, 2026
Merged

fix(agents): stabilize prompt cache fingerprints#60731
vincentkoc merged 5 commits into
mainfrom
codex/prompt-fingerprint-stability

Conversation

@vincentkoc

Copy link
Copy Markdown
Member

Summary

  • Problem: cache-relevant system prompt bytes could drift for semantically identical inputs because generated prompt sections preserved CRLF, trailing horizontal whitespace, and runtime capability ordering.
  • Why it matters: OpenAI-family and other prompt-byte-sensitive paths can miss KV/prompt-cache reuse even when the logical prompt did not change.
  • What changed: added conservative normalization for structured prompt sections, canonicalized runtime capability ids, and normalized post-boundary hook/addition text.
  • What did NOT change (scope boundary): no broad Unicode normalization, no user-message rewriting, and no mutation of injected project-context file contents.
  • AI-assisted: Codex.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: structured prompt assembly normalized too little. Generated sections such as extra system context, heartbeat text, skills catalog text, hook-provided system additions, and runtime capability lists could preserve byte-level noise that did not change semantics.
  • Missing detection / guardrail: there were no tests asserting byte-stable output across CRLF/LF, trailing-space noise, and equivalent capability-order permutations.
  • Contributing context (if known): earlier KV/cache work fixed transport/path divergence; this follow-up addresses prompt-byte drift inside the assembled system prompt itself.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/agents/system-prompt.test.ts
    • src/agents/system-prompt-cache-boundary.test.ts
    • src/agents/pi-embedded-runner/run/attempt.test.ts
  • Scenario the test should lock in: semantically equivalent CRLF/trailing-whitespace/capability-order inputs produce identical system-prompt bytes.
  • Why this is the smallest reliable guardrail: the bug is in prompt-string assembly, not provider transport behavior.
  • Existing test that already covers this (if any): existing cache-boundary tests cover split/strip, but not byte-stability of noisy structured inputs.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • None directly. This should improve cache reuse stability for equivalent prompt inputs.

Diagram (if applicable)

Before:
[generated prompt sections with CRLF / trailing spaces / reordered caps]
-> [different system prompt bytes]
-> [cache miss risk]

After:
[equivalent structured prompt inputs]
-> [canonicalized system prompt bytes]
-> [stable cache fingerprint]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local Node 22 / pnpm
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Build equivalent system prompts with CRLF/trailing-space noise in structured sections.
  2. Compare the resulting prompt bytes before and after normalization.
  3. Check hook-composed and post-boundary additions for the same behavior.

Expected

  • Equivalent structured inputs should yield identical cache-relevant prompt bytes.

Actual

  • Before this patch, equivalent inputs could differ by CRLF, trailing spaces, or runtime capability ordering.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios:
    • direct TS sanity probe for prompt-byte equality across noisy vs clean structured inputs
    • direct TS sanity probe for normalized boundary additions and hook-composed system context
    • git diff --check
  • Edge cases checked:
    • runtime capability dedupe + sort
    • CRLF normalization without broad prompt-body rewriting
    • boundary-preserving insertion of normalized dynamic additions
  • What you did not verify:
    • full Vitest targeted runner stayed non-reporting in this environment
    • pnpm build is currently blocked on unrelated existing latest-main type failures in src/agents/tools/sessions-spawn-tool.ts and src/plugin-sdk/core.ts

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: canonicalizing structured prompt sections could change intentionally preserved trailing whitespace in those sections.
    • Mitigation: scope is limited to generated/structured prompt sections and capability ids; injected project-context file contents remain untouched.

@vincentkoc vincentkoc self-assigned this Apr 4, 2026
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S maintainer Maintainer-authored PR labels Apr 4, 2026
@vincentkoc
vincentkoc marked this pull request as ready for review April 4, 2026 07:07
@greptile-apps

greptile-apps Bot commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds conservative normalization of structured prompt sections to stabilize prompt-cache fingerprints for OpenAI-family and other prompt-byte-sensitive transports. It introduces a new src/agents/prompt-cache-stability.ts utility with normalizeStructuredPromptSection (CRLF → LF, trailing horizontal whitespace stripping, trim) and normalizePromptCapabilityIds (deduplicate, lowercase, sort). These normalizers are then applied to extraSystemPrompt, skillsPrompt, heartbeatPrompt, workspaceNotes, modelAliasLines, hook-injected system context, and the post-boundary addition in prependSystemPromptAdditionAfterCacheBoundary. Runtime capability IDs in the prompt line are also canonicalized, while injected project-context file contents are deliberately left untouched.

Key observations:

  • The normalization scope is tight and well-justified: only structured/generated sections, not user-provided file content.
  • The new attempt.thread-helpers.ts cleanly extracts composeSystemPromptWithHookContext and applies the normalizer to hook-provided context without touching the base system prompt (which is already normalized at construction time).
  • Test coverage is thorough: byte-stable equivalence tests across CRLF/trailing-space/capability-order permutations are added in all three relevant test files.
  • Array.prototype.toSorted is used in normalizePromptCapabilityIds; this is available in Node.js 20+ and the repo mandates Node 22+, so this is safe.
  • runtimeCapabilitiesLower (used for the inlineButtonsEnabled runtime check) still uses its own inline String(cap).trim().toLowerCase() path rather than normalizePromptCapabilityIds — this is an intentional split: the inline path is for in-memory runtime behavior detection, while normalizePromptCapabilityIds is only for prompt string assembly.

Confidence Score: 5/5

This PR is safe to merge; all changes are additive normalizations with no behavioral impact on the prompt's logical content and are well-covered by new tests.

No P0 or P1 issues found. The change is narrowly scoped to structured prompt assembly, leaves user-injected file content untouched as documented, and is backed by byte-stability regression tests. Previously flagged issues in earlier review threads are pre-existing style observations, not blocking bugs.

No files require special attention.

Reviews (2): Last reviewed commit: "refactor(agents): simplify prompt capabi..." | Re-trigger Greptile

Comment thread src/agents/system-prompt.ts Outdated
Comment thread src/agents/system-prompt.ts
Comment thread src/agents/prompt-cache-stability.ts Outdated
@vincentkoc

Copy link
Copy Markdown
Member Author

@greptileai review

@vincentkoc
vincentkoc merged commit d75a893 into main Apr 4, 2026
20 of 37 checks passed
@vincentkoc
vincentkoc deleted the codex/prompt-fingerprint-stability branch April 4, 2026 07:20

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f61ff6390

ℹ️ 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/agents/prompt-cache-stability.ts Outdated
const seen = new Set<string>();
const normalized: string[] = [];
for (const capability of capabilities) {
const value = normalizeStructuredPromptSection(String(capability ?? "")).toLowerCase();

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.

P2 Badge Preserve capability ID casing in prompt normalization

Lowercasing each capability in normalizePromptCapabilityIds rewrites canonical IDs before they are rendered in the runtime line, so values like inlineButtons become inlinebuttons. This creates inconsistent prompt guidance (the same system prompt still references capabilities.inlineButtons) and can cause the agent to surface invalid or non-canonical capability names for camelCase/plugin-defined capabilities. Please dedupe/sort case-insensitively but keep the original canonical token when emitting capability IDs.

Useful? React with 👍 / 👎.

KimGLee pushed a commit to KimGLee/openclaw that referenced this pull request Apr 4, 2026
* fix(agents): stabilize prompt cache fingerprints

* chore(changelog): note prompt cache fingerprint stability

* refactor(agents): simplify capability normalization

* refactor(agents): simplify prompt capability normalization helper
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
* fix(agents): stabilize prompt cache fingerprints

* chore(changelog): note prompt cache fingerprint stability

* refactor(agents): simplify capability normalization

* refactor(agents): simplify prompt capability normalization helper
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
* fix(agents): stabilize prompt cache fingerprints

* chore(changelog): note prompt cache fingerprint stability

* refactor(agents): simplify capability normalization

* refactor(agents): simplify prompt capability normalization helper
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
* fix(agents): stabilize prompt cache fingerprints

* chore(changelog): note prompt cache fingerprint stability

* refactor(agents): simplify capability normalization

* refactor(agents): simplify prompt capability normalization helper
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
* fix(agents): stabilize prompt cache fingerprints

* chore(changelog): note prompt cache fingerprint stability

* refactor(agents): simplify capability normalization

* refactor(agents): simplify prompt capability normalization helper
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
* fix(agents): stabilize prompt cache fingerprints

* chore(changelog): note prompt cache fingerprint stability

* refactor(agents): simplify capability normalization

* refactor(agents): simplify prompt capability normalization helper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant