Skip to content

Commit d3e7b06

Browse files
fix(yaml): pin goose recipe prompt block-scalar indentation (#3343)
the goose recipe renderer emitted the prompt body under a bare '|' block scalar. yaml infers a plain block scalar's indentation from its first non-empty line, so a command body whose first line is itself indented (a markdown code block, a nested list item) made the parser expect that deeper indent for the whole block and reject the later, shallower lines - the generated .goose recipe then failed to parse. use an explicit '|2' indentation indicator so the block is always read at 2 spaces regardless of the body. added a regression test that round-trips an indented-first-line body through the yaml parser.
1 parent de6a04e commit d3e7b06

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

src/specify_cli/integrations/base.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,12 +1178,18 @@ def _render_yaml(cls, title: str, description: str, body: str, source_id: str) -
11781178
default_flow_style=False,
11791179
).strip()
11801180

1181-
# Indent the body for YAML block scalar
1181+
# Indent the body for YAML block scalar. Use an explicit indentation
1182+
# indicator ("|2") rather than a bare "|": YAML infers a plain block
1183+
# scalar's indentation from its first non-empty line, so a body whose
1184+
# first line is itself indented (e.g. a markdown code block or a nested
1185+
# list item) would make the parser expect that deeper indent for the
1186+
# whole block and reject the later, less-indented lines. Pinning the
1187+
# indent to 2 keeps the recipe parseable whatever the body looks like.
11821188
indented = "\n".join(f" {line}" for line in body.split("\n"))
11831189

11841190
lines = [
11851191
header_yaml,
1186-
"prompt: |",
1192+
"prompt: |2",
11871193
indented,
11881194
"",
11891195
f"# Source: {source_id}",

tests/integrations/test_integration_base_yaml.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,23 @@ def test_yaml_prompt_excludes_frontmatter(self, tmp_path, monkeypatch):
184184
assert "scripts:" not in parsed["prompt"]
185185
assert "---" not in parsed["prompt"]
186186

187+
def test_yaml_prompt_with_indented_first_line_stays_valid(self):
188+
"""A body whose first line is indented must still parse.
189+
190+
A bare ``|`` block scalar infers its indentation from the first
191+
non-empty line, so a body starting with an indented line (e.g. a
192+
markdown code block or nested list item) made the parser expect that
193+
deeper indent for the whole block and reject the later, shallower
194+
lines. The explicit ``|2`` indicator pins the indent so it parses."""
195+
body = " indented first line\nback to normal\n indented again"
196+
rendered = YamlIntegration._render_yaml("Title", "Desc", body, "src")
197+
198+
yaml_lines = [
199+
ln for ln in rendered.split("\n") if not ln.startswith("# Source:")
200+
]
201+
parsed = yaml.safe_load("\n".join(yaml_lines))
202+
assert parsed["prompt"].rstrip("\n") == body
203+
187204
def test_plan_command_has_no_context_placeholder(self, tmp_path):
188205
"""The generated plan command must not carry a context-file placeholder.
189206

0 commit comments

Comments
 (0)