Skip to content

fix(pformat): tag each argument with its slot number instead of counting empty slots - #5326

Open
yh928 wants to merge 3 commits into
tinyhumansai:mainfrom
yh928:fix/pformat-slot-indices
Open

fix(pformat): tag each argument with its slot number instead of counting empty slots#5326
yh928 wants to merge 3 commits into
tinyhumansai:mainfrom
yh928:fix/pformat-slot-indices

Conversation

@yh928

@yh928 yh928 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • P-Format calls now tag each value with the slot number it fills, so a sparse call sends only the arguments it means to send.
  • The parser refuses a call whose indices are missing, non-numeric, out of range, or unpaired, instead of binding values to whichever slots they land in.
  • Signatures carry the numbering, so the model reads the same layout the parser applies.
  • Also lands the two call-format changes this depends on: required-first slot ordering, and <name> placeholders in the rendered signature.
signature   get_weather[0|<location>|1|<unit>]
call        get_weather[0|London|1|metric]
sparse      get_weather[1|metric]
zero-arg    ping[]

Problem

See #5325. In short: the count of leading delimiters was load-bearing, and two live failures came from getting it wrong by one — GMAIL_LIST_THREADS[||50|<query>] failing schema validation 12 times in one turn, and a later call that shifted query and user_id one slot late and ran, searching Gmail with the account id set to the search text.

The second is the one that motivates the strictness here: it did not fail. A wrong call succeeded.

Solution

Indices remove the counting. There is nothing to miscount in [3|value|4|me].

Refusing beats guessing. An odd token count, a non-numeric index, or an index the schema has no parameter for returns None. That includes the old bare-positional form, deliberately: parsing it positionally is exactly the silent misbinding this replaces. The failure mode moves from a wrong call that succeeds to a malformed call the model is told about.

Repeated slots take the last value rather than failing the call — rare, and the later value is the model's latest intent.

Required-first ordering is kept, and is why the minimal call is name[0|value] rather than an arbitrary number the model has to look up.

One trap worth knowing about, now documented in the code

The typed-turn decoder's parse_pformat_call splits on raw | without honouring escapes. Its array is therefore a list of fragments whose join reconstructs the body, not a list of arguments — an escaped pipe arrives as two elements. Index-tagging those fragments numbers the halves of a single value.

An earlier revision of this PR did exactly that and an_escaped_pipe_stays_inside_its_argument caught it. The join must stay verbatim; the model's own indices ride inside it. Both the reconstruction sites now say so.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case)
  • Diff coverage ≥ 80% — the renderer, both parser branches, and every refusal path are covered
  • N/A: behaviour-only change, no feature row added/removed/renamed — Coverage matrix updated
  • No new external network dependencies introduced
  • N/A: no release-cut surface touched — Manual smoke checklist updated
  • Linked issue closed via Closes #NNN in the ## Related section

Testing

Six new cases, four of them failure paths:

  • an_out_of_range_slot_is_refused
  • an_odd_token_count_is_refused
  • a_bare_positional_call_is_refused_not_reinterpreted — the guarantee that replaces the old failure mode
  • the_live_gmail_call_binds_correctly_with_indices — the two live shapes, written with indices, plus an assertion that a stray delimiter is now refused rather than misbound
  • a_sparse_call_names_only_the_slots_it_fills
  • a_repeated_slot_takes_the_last_value

Plus the existing grammar suite rewritten to the new form (24 cases).

cargo test --lib: 12600 passed. The 2 remaining failures (tinyplace::manifest) reproduce on the base commit with no changes applied.

Impact

  • The taught form changes, so a model that has not read the new instructions writes a call the parser refuses. That is the intended trade: a refusal is recoverable in one round trip; a silent misbinding is not recoverable at all.
  • Sparse calls get shorter for the tools where this matters (2 filled slots out of 6 is [3|x|4|y] rather than [||||x|y]).
  • Not verified live yet beyond a successful GMAIL_LIST_THREADS turn: what a refusal reads like to the model, and whether it corrects in one turn, is the observation still owed.

Related

Closes #5325

Summary by CodeRabbit

  • Improvements
    • Tool calls now use numbered argument slots, allowing only required parameters to be submitted.
    • Optional arguments can be omitted without empty placeholders.
    • Improved validation rejects malformed, non-numeric, or out-of-range argument references.
    • Added support for escaped special characters in argument values.
  • Documentation
    • Updated tool-call examples, signatures, and guidance to explain indexed arguments and supported escapes.

@yh928
yh928 requested a review from a team August 2, 2026 23:54
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

P-Format tool calls now use explicit index|value pairs. Parameter ordering and signatures expose numbered slots. Parsing validates indices, supports sparse calls, omits empty values, and rejects legacy or malformed positional syntax.

Changes

Indexed P-Format arguments

Layer / File(s) Summary
Indexed argument contract and signatures
src/openhuman/agent/pformat.rs, src/openhuman/agent/dispatcher.rs, src/openhuman/agent/harness/subagent_runner/tool_prep.rs, src/openhuman/agent/prompts/*
P-Format documentation and instructions now define numbered `index
Indexed parsing and validation
src/openhuman/agent/pformat.rs
parse_call validates index/value pairs, rejects malformed and out-of-range indices, supports sparse calls, omits empty values, applies indexed coercion, and uses the final value for repeated slots. Tests cover these behaviors and signature round trips.
Dispatcher and fallback fixtures
src/openhuman/agent/dispatcher_tests.rs, src/openhuman/agent/tinyagents/model.rs
Tool-call fixtures now include explicit argument indices while preserving expected parsed arguments.

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

Sequence Diagram(s)

sequenceDiagram
  participant Model
  participant Dispatcher
  participant parse_call
  participant PFormatToolParams
  Model->>Dispatcher: indexed P-Format tool call
  Dispatcher->>parse_call: tokenize index/value pairs
  parse_call->>PFormatToolParams: resolve indexed parameters and types
  PFormatToolParams-->>parse_call: ordered parameter definitions
  parse_call-->>Dispatcher: parsed named arguments
  Dispatcher-->>Model: tool-call result
Loading

Possibly related PRs

Suggested labels: agent, bug, rust-core

Suggested reviewers: senamakel

Poem

I hop through numbered slots with care,
No empty gaps drift through the air.
Sparse calls land where they belong,
Bad indices cannot run along.
Every argument finds its seat. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: P-Format arguments now use explicit slot numbers instead of empty-slot counting.
Linked Issues check ✅ Passed The changes implement indexed sparse calls, reject malformed and legacy positional forms, and prevent silent parameter misbinding required by #5325.
Out of Scope Changes check ✅ Passed The parser, prompt, documentation, and regression-test changes directly support the indexed P-Format objectives in #5325.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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 added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Aug 2, 2026

@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: 58135af16f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/openhuman/agent/pformat.rs

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/openhuman/agent/pformat.rs (1)

419-425: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider splitting the growing mod tests into a separate file.

This file now extends past 768 lines including its inline test module, exceeding the guideline to "Prefer Rust modules of approximately 500 lines or fewer and maintain small, single-responsibility Unix-style modules." The sibling dispatcher_tests.rs already establishes the pattern of externalizing tests from their production module in this directory. Moving mod tests here into a pformat_tests.rs (or similar) would bring pformat.rs back under the guideline without touching test content.

🤖 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 `@src/openhuman/agent/pformat.rs` around lines 419 - 425, The inline mod tests
in pformat.rs has grown the production module beyond the preferred size. Move
the entire tests module, including make_registry and all existing test content,
into a sibling external test file such as pformat_tests.rs; declare or include
that test module from pformat.rs using the established dispatcher_tests.rs
pattern, without changing test behavior.

Source: Coding guidelines

🤖 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 `@src/openhuman/agent/pformat.rs`:
- Around line 279-341: Update the parse_response call path and the rejection
tracing::debug! events in the p-format parsing flow to include an available turn
or request correlation identifier alongside tool. Thread the identifier from the
session loop’s ChatResponse context through the relevant parser functions, or
remove these rejection logs if no identifier can be provided; do not retain
uncorrelated "[pformat]" rejection events.

---

Nitpick comments:
In `@src/openhuman/agent/pformat.rs`:
- Around line 419-425: The inline mod tests in pformat.rs has grown the
production module beyond the preferred size. Move the entire tests module,
including make_registry and all existing test content, into a sibling external
test file such as pformat_tests.rs; declare or include that test module from
pformat.rs using the established dispatcher_tests.rs pattern, without changing
test behavior.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d62f8755-b921-43ff-996f-7dee983893d4

📥 Commits

Reviewing files that changed from the base of the PR and between a40ba85 and 58135af.

📒 Files selected for processing (4)
  • src/openhuman/agent/dispatcher.rs
  • src/openhuman/agent/dispatcher_tests.rs
  • src/openhuman/agent/pformat.rs
  • src/openhuman/tinyagents/model.rs

Comment thread src/openhuman/agent/pformat.rs
@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces P-Format's bare-positional call syntax (name[arg1|arg2|...]) with a slot-indexed form (name[0|value|1|value|...]) to eliminate the off-by-one delimiter counting that caused two live failures: a 12-retry schema loop and a call that silently bound the Gmail search text to the account-id slot. The parser now rejects odd token counts, non-numeric indices, and out-of-range slots instead of guessing, moving the failure mode from a wrong call that succeeds to a malformed call the model is told about.

  • pformat.rsparse_call is fully rewritten to consume index/value pairs; from_schema now orders required parameters first (in schema-declared order), then optional ones alphabetically, so slot 0 is always the most-likely argument a model needs to send.
  • render_signature generates numbered <name> placeholders (get_weather[0|<location>|1|<unit>]), giving the model the exact indices to echo back.
  • All instruction strings in dispatcher.rs, tool_prep.rs, and render_helpers.rs are updated to teach the new form, with the empty-slot rule removed.

Confidence Score: 5/5

Safe to merge — the change hardens parse behavior by making every rejection explicit and every binding unambiguous.

The core logic is carefully designed: the even-token guard, numeric-index requirement, range check, and empty-value skip each map to a distinct real failure that was observed or anticipated. The escape path in split_pipes correctly handles escaped pipes, so values containing literal pipes survive the parity check. Required-first ordering is deterministic and tested against both single and multi-required schemas. All six new tests cover the failure paths that motivated the change, and the 24 existing grammar tests have been updated to the new form and pass. The only gap is a stale doc-comment example on render_signature, which is cosmetic.

Files Needing Attention: No files require special attention. The render_signature doc-comment example in pformat.rs is missing slot numbers, but that is cosmetic. The typed-turn decoder in model.rs (parse_pformat_call, not modified here) is documented in the PR as correctly joining fragments verbatim before calling parse_call, and the existing escape test guards that path.

Important Files Changed

Filename Overview
src/openhuman/agent/pformat.rs Core parsing module rewritten: slot-indexed format replaces bare-positional, parse_call now rejects odd token counts, non-numeric indices, and out-of-range slots; from_schema reordered to required-first then optional alphabetically; render_signature generates numbered placeholders
src/openhuman/agent/dispatcher.rs System-prompt instructions updated to teach the model the new indexed call form; example call and rules text updated, empty-slot rule removed
src/openhuman/agent/dispatcher_tests.rs Existing dispatcher tests updated to new name[index
src/openhuman/agent/harness/subagent_runner/tool_prep.rs Subagent tool instructions updated to the new indexed format, mirroring the main dispatcher changes
src/openhuman/agent/prompts/render_helpers.rs Subagent system prompt updated with new indexed call example and description; safe mechanical update
src/openhuman/agent/prompts/types.rs Single doc-comment update to PFormat variant description; trivial
src/openhuman/tinyagents/model.rs One test updated to use new indexed form for the legacy p-format fallback path; no logic change

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Model emits name[body]"] --> B[parse_call]
    B --> C{Tool in registry?}
    C -- No --> Z1[return None]
    C -- Yes --> D[split_pipes body - honours escapes]
    D --> E{token count even?}
    E -- No --> Z2[return None - odd token count]
    E -- Yes --> F[chunks_exact 2 - index and value pairs]
    F --> G{raw_index parses as usize?}
    G -- No --> Z3[return None - non-numeric or bare-positional]
    G -- Yes --> H{slot within params.len?}
    H -- No --> Z4[return None - out-of-range slot]
    H -- Yes --> I{value.trim is empty?}
    I -- Yes --> J[skip - argument omitted]
    I -- No --> K[coerce_value per schema type]
    K --> L[insert into args map - last-write-wins]
    J --> M{more pairs?}
    L --> M
    M -- Yes --> F
    M -- No --> N[return Some - name and args]
Loading

Reviews (2): Last reviewed commit: "fix(pformat): teach the sub-agent prompt..." | Re-trigger Greptile

yh928 added a commit to yh928/openhuman that referenced this pull request Aug 5, 2026
…arser reads

The parser now requires a slot number before each value, and returns `None` for
a call that does not carry one. The main dispatcher's protocol block was updated
with it; two sub-agent paths were not, so a sub-agent was told to write the old
grammar and every tool call it made was dropped.

- `subagent_runner/tool_prep.rs` taught `name[arg1|arg2|...|argN]` with
  "leave a slot empty to omit that argument" — the empty-slot counting this
  change exists to remove.
- `render_helpers.rs`'s sub-agent protocol block taught `tool_name[arg1|arg2]`
  and "match the order shown ... (alphabetical by parameter name)".

Both now carry the same two rules the main block does: a `Call as:` signature
numbers its slots, and you send only the arguments you are passing, each with
its number. The worked example is the same one (`get_weather[0|London|1|metric]`)
so the three blocks cannot drift into describing different grammars.

Also drops the two references the prompts had no business making — a section
heading by name (`## Tools`) and a relative position ("signature above") — since
neither survives a reordered or filtered prompt.

Reported by codex on tinyhumansai#5326 (P1).
@coderabbitai coderabbitai Bot added the rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. label Aug 5, 2026

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

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 `@src/openhuman/agent/harness/subagent_runner/tool_prep.rs`:
- Around line 90-101: Use “indexed” consistently instead of “positional” in the
sub-agent protocol wording. Update
src/openhuman/agent/harness/subagent_runner/tool_prep.rs lines 90-101 and
src/openhuman/agent/prompts/render_helpers.rs lines 437-440, while preserving
the existing indexed P-Format examples and rules.

In `@src/openhuman/agent/prompts/render_helpers.rs`:
- Around line 437-440: Update the sub-agent prompt text near the tool-call
example to document the P-Format escape for representing a literal backslash,
alongside the existing \| and \] guidance. Keep this block consistent with the
main text-mode instructions without changing the surrounding
argument-substitution behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 16db9bab-3eba-4273-aefb-9b520d1f7a31

📥 Commits

Reviewing files that changed from the base of the PR and between 58135af and 12fc086.

📒 Files selected for processing (3)
  • src/openhuman/agent/harness/subagent_runner/tool_prep.rs
  • src/openhuman/agent/prompts/render_helpers.rs
  • src/openhuman/agent/prompts/types.rs

Comment thread src/openhuman/agent/harness/subagent_runner/tool_prep.rs Outdated
Comment thread src/openhuman/agent/prompts/render_helpers.rs
…ing empty slots

P-Format was bare positional, and a skipped argument was written as an empty
slot (`name[||value]`). That made the *count of leading delimiters*
load-bearing, and it is the single thing models get wrong most:

- `GMAIL_LIST_THREADS[||50|<query>]` failed schema validation 12 times in one
  turn before the turn was cut short.
- A live `GMAIL_LIST_THREADS` wrote four leading empties where three were
  needed, so `query` and `user_id` each landed one slot late, in `user_id` and
  `verbose`. The call RAN, searching Gmail with the account id set to the
  search text.

Both are off-by-one on a delimiter, and both bound arguments to the wrong
parameter silently. The signature now numbers its slots and a call tags each
value with the slot it fills:

    signature  get_weather[0|<location>|1|<unit>]
    call       get_weather[0|London|1|metric]
    sparse     get_weather[1|metric]

Only the arguments being sent appear, so there is nothing to miscount.

The parser refuses rather than guesses: a missing, non-numeric, or
out-of-range index, or an odd token count, returns None. The old bare
positional form is therefore rejected too — deliberately, since parsing it
positionally would resurrect the exact failure this replaces. The failure mode
moves from a wrong call that succeeds to a malformed call the model is told
about.

Required-first ordering is kept: it is why the minimal call is `name[0|value]`
rather than an arbitrary index.

Note for future edits to the typed-turn decoder: `parse_pformat_call` splits on
raw `|` WITHOUT honouring escapes, so its array is a list of fragments whose
join reconstructs the body, not a list of arguments. An escaped pipe arrives as
two elements. Tagging those fragments numbers the halves of one value; the
join must stay verbatim, and the model's own indices ride inside it. A test
covers it (`an_escaped_pipe_stays_inside_its_argument`).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy
yh928 added a commit to yh928/openhuman that referenced this pull request Aug 5, 2026
…arser reads

The parser now requires a slot number before each value, and returns `None` for
a call that does not carry one. The main dispatcher's protocol block was updated
with it; two sub-agent paths were not, so a sub-agent was told to write the old
grammar and every tool call it made was dropped.

- `subagent_runner/tool_prep.rs` taught `name[arg1|arg2|...|argN]` with
  "leave a slot empty to omit that argument" — the empty-slot counting this
  change exists to remove.
- `render_helpers.rs`'s sub-agent protocol block taught `tool_name[arg1|arg2]`
  and "match the order shown ... (alphabetical by parameter name)".

Both now carry the same two rules the main block does: a `Call as:` signature
numbers its slots, and you send only the arguments you are passing, each with
its number. The worked example is the same one (`get_weather[0|London|1|metric]`)
so the three blocks cannot drift into describing different grammars.

Also drops the two references the prompts had no business making — a section
heading by name (`## Tools`) and a relative position ("signature above") — since
neither survives a reordered or filtered prompt.

Reported by codex on tinyhumansai#5326 (P1).
@yh928
yh928 force-pushed the fix/pformat-slot-indices branch from 12fc086 to 8929270 Compare August 5, 2026 01:54
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

yh928 added a commit to yh928/openhuman that referenced this pull request Aug 5, 2026
…arser reads

The parser now requires a slot number before each value, and returns `None` for
a call that does not carry one. The main dispatcher's protocol block was updated
with it; two sub-agent paths were not, so a sub-agent was told to write the old
grammar and every tool call it made was dropped.

- `subagent_runner/tool_prep.rs` taught `name[arg1|arg2|...|argN]` with
  "leave a slot empty to omit that argument" — the empty-slot counting this
  change exists to remove.
- `render_helpers.rs`'s sub-agent protocol block taught `tool_name[arg1|arg2]`
  and "match the order shown ... (alphabetical by parameter name)".

Both now carry the same two rules the main block does: a `Call as:` signature
numbers its slots, and you send only the arguments you are passing, each with
its number. The worked example is the same one (`get_weather[0|London|1|metric]`)
so the three blocks cannot drift into describing different grammars.

Also drops the two references the prompts had no business making — a section
heading by name (`## Tools`) and a relative position ("signature above") — since
neither survives a reordered or filtered prompt.

Reported by codex on tinyhumansai#5326 (P1).
@yh928
yh928 force-pushed the fix/pformat-slot-indices branch from 8929270 to f7182df Compare August 5, 2026 02:10

@greptile-apps greptile-apps 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

…arser reads

The parser now requires a slot number before each value, and returns `None` for
a call that does not carry one. The main dispatcher's protocol block was updated
with it; two sub-agent paths were not, so a sub-agent was told to write the old
grammar and every tool call it made was dropped.

- `subagent_runner/tool_prep.rs` taught `name[arg1|arg2|...|argN]` with
  "leave a slot empty to omit that argument" — the empty-slot counting this
  change exists to remove.
- `render_helpers.rs`'s sub-agent protocol block taught `tool_name[arg1|arg2]`
  and "match the order shown ... (alphabetical by parameter name)".

Both now carry the same two rules the main block does: a `Call as:` signature
numbers its slots, and you send only the arguments you are passing, each with
its number. The worked example is the same one (`get_weather[0|London|1|metric]`)
so the three blocks cannot drift into describing different grammars.

Also drops the two references the prompts had no business making — a section
heading by name (`## Tools`) and a relative position ("signature above") — since
neither survives a reordered or filtered prompt.

Reported by codex on tinyhumansai#5326 (P1).
@yh928
yh928 force-pushed the fix/pformat-slot-indices branch from f7182df to d1cdbe3 Compare August 5, 2026 02:47

@greptile-apps greptile-apps 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

…jections

The sub-agent block documented `\|` and `\]` but not `\\`, which
`split_pipes` also decodes. That is the same defect this PR opened to fix, one
notch quieter: where the slot numbering drifted and calls were dropped whole, an
undocumented escape leaves a value carrying a backslash encoded on a guess. All
three P-Format prompt blocks now name the same three escapes.

Adds a test that asserts the sub-agent block documents every escape the parser
honours, so the two cannot drift again without something failing.

The rejection logs now carry a correlation field. `parse_call` is pure and takes
no context, but it runs inside the turn's task, so the ambient chat thread id is
readable from `thread_context` without threading an argument through a parser
that has no other use for one — `<none>` outside a turn. Dropping the logs
instead was the wrong trade: they are the only signal that a model's call was
refused, which is exactly the failure this PR is about.

agent::pformat 24, agent::prompts 72 pass.

Reported by CodeRabbit on tinyhumansai#5326.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy

@greptile-apps greptile-apps 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

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

🧹 Nitpick comments (1)
src/openhuman/agent/prompts/mod_tests.rs (1)

793-797: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Clean up the temporary workspace after the test.

This test creates a UUID-named directory under std::env::temp_dir() and never removes it. Repeated test runs leave directories behind. Use a scoped temporary-directory helper or cleanup guard.

🤖 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 `@src/openhuman/agent/prompts/mod_tests.rs` around lines 793 - 797, Update the
test around the workspace setup to use a scoped temporary-directory helper or
cleanup guard, ensuring the UUID-named directory is removed when the test exits,
including on failure; preserve the existing workspace path usage.
🤖 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.

Nitpick comments:
In `@src/openhuman/agent/prompts/mod_tests.rs`:
- Around line 793-797: Update the test around the workspace setup to use a
scoped temporary-directory helper or cleanup guard, ensuring the UUID-named
directory is removed when the test exits, including on failure; preserve the
existing workspace path usage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 82bb0574-12a5-47ba-82ea-75629bcb138f

📥 Commits

Reviewing files that changed from the base of the PR and between d75b0a4 and 278bbc0.

📒 Files selected for processing (8)
  • src/openhuman/agent/dispatcher.rs
  • src/openhuman/agent/dispatcher_tests.rs
  • src/openhuman/agent/harness/subagent_runner/tool_prep.rs
  • src/openhuman/agent/pformat.rs
  • src/openhuman/agent/prompts/mod_tests.rs
  • src/openhuman/agent/prompts/render_helpers.rs
  • src/openhuman/agent/prompts/types.rs
  • src/openhuman/agent/tinyagents/model.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/openhuman/agent/prompts/types.rs
  • src/openhuman/agent/tinyagents/model.rs
  • src/openhuman/agent/prompts/render_helpers.rs
  • src/openhuman/agent/dispatcher.rs
  • src/openhuman/agent/dispatcher_tests.rs
  • src/openhuman/agent/harness/subagent_runner/tool_prep.rs
  • src/openhuman/agent/pformat.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

P-Format binds arguments to the wrong parameters when a model miscounts empty slots

1 participant