expandPrompt: docstring claim about 'body last' protection is incomplete; placeholders in earlier variable values are still expanded
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- tooling
Research direction
Start in src/util/expand-prompt.ts, where the per-key replacement loop is described, and review the matching variable construction in src/prompt-generators/github.ts and gitlab.ts. Run the plain Node reproduction from the issue, then verify that placeholder-looking text in variable values remains unchanged or that the comments accurately document the partial guarantee.
Written by the indexing model from the issue text.
Description
Observed behavior
buildVariables in src/prompt-generators/github.ts (and the matching helper in src/prompt-generators/gitlab.ts) intentionally places body/description last in the returned object, with this comment:
Keep
bodylast so any placeholder-looking text inside the issue body is inserted after replacement of other variables has finished.
The implementation in expandPrompt iterates Object.entries(variables) and runs result.replaceAll('{{key}}', value) for each, so this ordering does protect against an issue body that contains, say, {{title}} (the body is substituted last, after {{title}} is already gone).
The protection only goes one way, though. Earlier variable values whose text contains {{body}} are still re-substituted in the next iteration. Example:
let result = 'Title: {{title}}\nBody: {{body}}';
const variables = {
title: 'fix {{body}} formatting', // mentions another placeholder
body: 'this is the body text',
};
for (const [key, value] of Object.entries(variables)) {
result = result.replaceAll('{{' + key + '}}', value);
}
// result:
// Title: fix this is the body text formatting
// Body: this is the body text
The {{body}} inside the title placeholder text got expanded to the body content because the title substitution happened first and left a {{body}} token in the template, which the later body substitution then replaced.
This contradicts what the docstring tells the reader to expect (body last makes placeholder-looking text safe). It is unlikely to bite in practice for real GitHub issue titles, but it is exactly the failure mode the comment claims to prevent, and the same shape is duplicated in gitlab.ts.
Expected behavior
Either:
- Variable substitution is a single pass that never re-substitutes the result of a previous substitution (so placeholder-looking text in any variable value is preserved verbatim), and the docstring describes that guarantee; or
- The docstring is updated to clearly describe the partial protection (
bodylast protects against placeholders inside the body, but not against{{body}}appearing inside other variable values).
A single-pass substitution can be done by scanning for {{...}} tokens in result once and looking each up in the variable map, falling through unknown names unchanged.
Minimal reproduction
See snippet above; reproducible in plain Node.
Suggested fix
Replace the per-key replaceAll loop in src/util/expand-prompt.ts with a single-pass token substitution:
const VARIABLE_PATTERN = /\{\{([a-zA-Z0-9_]+)\}\}/g;
result = result.replace(VARIABLE_PATTERN, (match, name) =>
Object.hasOwn(variables, name) ? variables[name] : match,
);
This also incidentally fixes the related $-pattern corruption (filed separately) because the function form of replace does not interpret $ patterns.
If the single-pass approach is not desired, at minimum update the Keep \body` last...comment ingithub.tsandgitlab.ts` to describe the actual (one-directional) guarantee.
- Dominant language
- TypeScript
- Stars
- 2
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from joewalker/loop-the-loop
-
bug S4
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
joewalker/loop-the-loop#88 ·
-
Git exec(): child killed by a signal rejects with new Error('') and loses the signal information Openbug S3
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
joewalker/loop-the-loop#84 ·
-
bug S3
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
joewalker/loop-the-loop#83 ·
-
bug S3
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
joewalker/loop-the-loop#82 ·
-
bug S4
Difficulty 1/5 Under an hour Newbie friendliness 90/100
joewalker/loop-the-loop#79 ·
All issues in joewalker/loop-the-loop
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100