Skip to content

Prompt bodies in the execution instructions use 3-backtick fences, so a prompt containing a code block truncates the document #1064

Description

@breaking-brake

Found while building #1063 (QA loop). Verified by running generateExecutionInstructions against packages/core/src/services/workflow-prompt-generator.ts on auto-qa, which is identical to origin/main for this code.

Sibling of #1026 — same root cause, different sites. #1026 covers the MCP node's User Intent fence (:423-425) and the metadata comment; this issue covers the three user-prompt sites in the same file, which #1026 does not mention.

The sites

generateExecutionInstructions embeds user-typed prompt text inside plain 3-backtick fences, with no escaping and no widening of the fence:

Line Node Field
:776-778 Sub-Agent **Prompt**node.data.prompt
:886-888 Codex **Prompt**node.data.prompt
:934-936 Prompt body, inside ### Prompt Node Details

There is a fourth exposure on the same input: the Codex **Execution Command** block at :868-872 opens with ```bash and interpolates the same prompt into the codex exec … '<prompt>' argument. escapedPrompt (:861) escapes single quotes for the shell but does nothing about backticks, so a prompt containing a fence breaks that block too — and the command a user is instructed to run is then wrong as well as unfenced.

Observed

For a Prompt node whose body is:

before
```bash
ls -la
```
after

the generator emits:

#### p1(before)

````
before
```bash
ls -la
```
after
````

The inner ``` closes the block, after lands at document top level, and the intended closing fence opens a new one. With a second node in the workflow, that stray fence swallows the whole of the next node's section — heading and body — so the agent never sees it. A prompt whose last line is exactly ``` does the same thing with no code block involved at all.

Identical behaviour reproduced at all three sites (Sub-Agent **Prompt**, Codex **Prompt**, Prompt node body) and in the Codex **Execution Command** block.

Impact

Every export surface is affected — ccwf render, the MCP render_workflow tool, the canvas "Copy as Markdown" action, and all seven export targets go through this one generator. A prompt containing a code block is ordinary input, not a corner case: prompts that ask an agent to produce or modify code routinely contain one.

Nothing on the user's machine reports it. The export succeeds, the file is written, and the failure surfaces wherever the agent later runs — reading part of the instruction document as prose and, in the multi-node case, never seeing a node at all.

Suggested fix

workflow-overview-formatter.ts:576 already solved this in the same directory:

function fence(text: string): string {
  // Use 4-backtick fence to safely contain triple-backtick blocks inside prompts.
  return `\`\`\`\`\n${text}\n\`\`\`\``;
}

It is applied at :288, :294, :315, :394, :426, :431, :460 and pinned by its own suite (workflow-overview-formatter.test.ts:157). The fix is to reuse it — two independently maintained generators currently disagree on the same input. #1026's User Intent site wants the same treatment, so one shared helper closes both issues.

Caveat worth deciding on: a 4-backtick fence is not a complete answer. A prompt containing four backticks defeats it exactly the way three defeats the current code — verified, and pinned as observed in the #1063 suite. The robust form measures the longest backtick run in the text and opens with one more.

Fix the Codex **Execution Command** block in the same change. Not optional bookkeeping: it is measured. Patching only the three **Prompt** sites leaves the Codex site's pins in the #1063 suite green, because the unfixed exec block breaks the document first and masks them — so a partial fix looks complete to the test suite. Widening that fence repairs the document, but note the interpolated codex exec command line stays wrong for a prompt containing a backtick, since only ' is escaped.

Test status

#1063 landed the fence cases passing, named CURRENT BEHAVIOUR (bug #1064) (merged to auto-qa in #1065). They assert what the code does today, so they turn red when this is fixed. That is the signal to come back and flip them to the intended contract; the suite's topLevelLines helper already expresses the correct property — the whole prompt stays inside one fenced region — so the flip is a small edit, not a rewrite.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions