Skip to content

fix(router): preserve literal multiline and Windows-path dispatch payloads#481

Open
shaun0927 wants to merge 1 commit intoQ00:mainfrom
shaun0927:fix/router-preserve-literal-payloads
Open

fix(router): preserve literal multiline and Windows-path dispatch payloads#481
shaun0927 wants to merge 1 commit intoQ00:mainfrom
shaun0927:fix/router-preserve-literal-payloads

Conversation

@shaun0927
Copy link
Copy Markdown
Collaborator

Summary

  • preserve multiline inline payloads for /ouroboros:<skill> dispatch by letting the shared parser carry newline-containing remainders
  • preserve exact Windows literal path payloads when shell normalization would otherwise strip backslashes
  • add focused regression coverage at the parser, argument-extraction, and resolved-dispatch levels

Closes #479.
Refs #480.

Problem

The shared router introduced in #459 normalizes intercepted remainders through a POSIX shell path:

  • parse_ooo_command() only matches a single-line remainder because the regex does not span newlines
  • extract_first_argument() always round-trips the remainder through shlex.split() and rejoins it

That is fine for natural-language prompts, but it breaks literal payload contracts in two ways:

  1. /ouroboros:run with multiline inline YAML falls out of the deterministic dispatch path entirely, even though skills/run/SKILL.md documents seed_file_or_content and the MCP tool already supports inline seed_content
  2. Windows-style path payloads lose their backslashes before they reach the target MCP tool

Because the router is now shared, both failures affect every runtime using deterministic skill dispatch.

What changed

src/ouroboros/router/command_parser.py

  • enable multiline remainder capture by compiling the shared command regex with re.DOTALL

src/ouroboros/router/dispatch.py

  • preserve newline-containing remainders exactly instead of normalizing them through shell splitting
  • preserve exact Windows literal path payloads (C:\..., \\server\share\...) for the same reason
  • keep the existing natural-language normalization path for ordinary single-line prompts

Tests

  • tests/unit/router/test_command_parser.py
    • multiline /ouroboros:run remainder stays attached to the command
  • tests/unit/router/test_extract_first_argument.py
    • multiline YAML payloads stay exact
    • Windows drive-letter and UNC paths keep their backslashes
  • tests/unit/router/test_valid_dispatch_normalization.py
    • resolved dispatch preserves multiline inline seed content
    • resolved dispatch preserves Windows literal path payloads end-to-end

Why this PR is intentionally narrow

Recent merged fixes in this repo tend to land best when they:

  • fix one shared-layer boundary at a time
  • include focused regression tests
  • avoid broad refactors while the bug surface is still small

This PR follows that shape. It does not change skill routing policy, runtime-specific behavior, or MCP tool contracts. It only preserves router payloads that current docs and tool definitions already imply should survive dispatch unchanged.

Verification

  • PYTHONPATH=src python3 -m pytest tests/unit/router/test_command_parser.py tests/unit/router/test_extract_first_argument.py tests/unit/router/test_valid_dispatch_normalization.py -q
  • PYTHONPATH=src python3 -m pytest tests/unit/router -q
  • PYTHONPATH=src python3 -m pytest tests/integration/test_codex_skill_smoke.py tests/integration/test_codex_skill_fallback.py -q
  • python3 -m compileall src/ouroboros/router tests/unit/router/test_command_parser.py tests/unit/router/test_extract_first_argument.py tests/unit/router/test_valid_dispatch_normalization.py

Non-goals

  • full native-Windows support beyond the shared router payload boundary
  • changing the documented standalone ouroboros run <seed.yaml> CLI contract
  • changing natural-language normalization for ordinary single-line skill prompts

… contracts

The shared router normalized every intercepted remainder through POSIX
shell splitting. That kept natural-language prompts tidy, but it also
silently changed payloads that are supposed to be forwarded verbatim,
notably multiline inline seed content and Windows-style paths.

This patch keeps the shared router narrow: multiline remainders are now
recognized by the parser, and exact literal payloads are preserved only
for cases where shell normalization is destructive. The existing
natural-language normalization path stays intact for the common single-
line prompt forms.

Constraint: Recent router fixes favor small shared-layer corrections with focused regression coverage
Rejected: Split multiline and Windows-path handling into separate PRs | both regressions come from the same remainder-preservation boundary
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep router dispatch exact for literal payloads; do not reintroduce shell normalization on newline-containing or Windows-literal inputs without contract tests
Tested: pytest tests/unit/router/test_command_parser.py tests/unit/router/test_extract_first_argument.py tests/unit/router/test_valid_dispatch_normalization.py -q; pytest tests/unit/router -q; pytest tests/integration/test_codex_skill_smoke.py tests/integration/test_codex_skill_fallback.py -q; python -m compileall src/ouroboros/router tests/unit/router/test_command_parser.py tests/unit/router/test_extract_first_argument.py tests/unit/router/test_valid_dispatch_normalization.py
Not-tested: Full repository test suite
Related: Q00#479
Related: Q00#480
Copy link
Copy Markdown
Contributor

@ouroboros-agent ouroboros-agent Bot left a comment

Choose a reason for hiding this comment

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

Review — ouroboros-agent[bot]

Verdict: REQUEST_CHANGES

Reviewing commit b5dcc1a for PR #481

Review record: 10f72bd8-6035-4e4a-9c1c-ab56f2d5dbed

Blocking Findings

| # | File:Line | Severity | Finding |
|### Recovery Notes
First recoverable review artifact generated from codex analysis log.

---|-----------|----------|---------|
| 1 | src/ouroboros/router/dispatch.py:328 | BLOCKING | The new multiline remainder support does not round-trip through the public ParsedOooCommand entrypoint. resolve_skill_dispatch(parsed, ...) and resolve_parsed_skill_dispatch(parsed, prompt=None, ...) reconstruct Resolved.prompt with f"{parsed.command_prefix} {parsed.remainder}", which inserts a space even when the original separator was a newline. For multiline inline seed content, callers that pass a parsed command now get a different prompt than the one they parsed, losing the exact payload layout that this PR is trying to preserve. |

Non-blocking Suggestions

| 1 | tests/unit/router/test_valid_dispatch_normalization.py:151 | tests | Add a regression test that resolves a multiline ParsedOooCommand directly, not just a raw prompt string. That is the public path currently missing coverage, and it would catch the prompt reconstruction issue above. |

Design Notes

The change is narrowly targeted and the parser/extractor split still makes sense, but the new multiline capability is only wired through the raw-prompt flow. The parsed-command API needs the same exact-text preservation guarantee to keep the router’s public contract consistent.


Reviewed by ouroboros-agent[bot] via Codex deep analysis

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.

router: /ouroboros:run does not dispatch multiline inline seed content

1 participant