From 88aae16b32f8847ada4e5015a1cac5019ce7d102 Mon Sep 17 00:00:00 2001 From: "mateus.cardoso" Date: Fri, 24 Jul 2026 21:57:53 -0300 Subject: [PATCH] fix(agent-context): apply default markers when config markers are blank (bash) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the extension config omits context_markers (or sets them blank), relying on the built-in defaults, the Bash port aborted with "malformed config parser output" and never updated the context file, while the Python (`or DEFAULT_*`) and PowerShell (default-initialized) ports handled it correctly. The config parser prints three lines (context_files JSON, marker_start, marker_end), captured via `_raw_opts="$(...)"`. Command substitution strips trailing newlines, so blank marker lines collapse the output to fewer than three, tripping the `(( ${#_opts_lines[@]} < 3 ))` guard and making the DEFAULT_START/END substitution unreachable — the exact case it was written for. Require only the context_files line and default the marker lines to empty (`${_opts_lines[1]:-}` / `${_opts_lines[2]:-}`) so the existing DEFAULT_START/END fallback fills them in. Add a parity regression test with blank markers (it fails on the old guard and passes with the fix). Co-Authored-By: Claude Opus 4.8 --- .../scripts/bash/update-agent-context.sh | 13 +++++++--- ...test_update_agent_context_python_parity.py | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/extensions/agent-context/scripts/bash/update-agent-context.sh b/extensions/agent-context/scripts/bash/update-agent-context.sh index 747a47a16d..7fbe3ef49a 100755 --- a/extensions/agent-context/scripts/bash/update-agent-context.sh +++ b/extensions/agent-context/scripts/bash/update-agent-context.sh @@ -176,13 +176,18 @@ _opts_lines=() while IFS= read -r _line || [[ -n "$_line" ]]; do _opts_lines+=("$_line") done < <(printf '%s\n' "$_raw_opts") -if (( ${#_opts_lines[@]} < 3 )); then - echo "agent-context: malformed config parser output; expected 3 lines (context_files, marker_start, marker_end), got ${#_opts_lines[@]}; skipping update." >&2 +if (( ${#_opts_lines[@]} < 1 )); then + echo "agent-context: malformed config parser output; expected at least the context_files line, got ${#_opts_lines[@]}; skipping update." >&2 exit 0 fi +# The marker lines may be absent: the $(...) capture above strips trailing +# newlines, so blank markers (the config omitting context_markers and relying on +# defaults) collapse the 3-line output to fewer lines. Default them to empty here +# and let the DEFAULT_START/END substitution below fill them in, matching the +# Python and PowerShell ports. CONTEXT_FILES_JSON="${_opts_lines[0]}" -MARKER_START="${_opts_lines[1]}" -MARKER_END="${_opts_lines[2]}" +MARKER_START="${_opts_lines[1]:-}" +MARKER_END="${_opts_lines[2]:-}" if ! _context_files_raw="$("$_python" - "$CONTEXT_FILES_JSON" <<'PY' import json diff --git a/tests/extensions/test_update_agent_context_python_parity.py b/tests/extensions/test_update_agent_context_python_parity.py index 92e8e20f8b..32a3dff1ea 100644 --- a/tests/extensions/test_update_agent_context_python_parity.py +++ b/tests/extensions/test_update_agent_context_python_parity.py @@ -222,6 +222,32 @@ def test_python_custom_markers_matching_bash(tmp_path: Path) -> None: assert "old" not in content +@requires_posix_bash +def test_python_blank_markers_use_defaults_matching_bash(tmp_path: Path) -> None: + # Regression: with blank markers (config relying on the built-in defaults), + # the Bash port must fall back to DEFAULT_START/END, matching the Python and + # PowerShell ports. Previously the Bash config-parser transport dropped the + # trailing empty marker lines under $(...) command substitution, tripping the + # "malformed config parser output" guard so the default-marker substitution + # became unreachable and the context file was never updated. + markers = {"start": "", "end": ""} + repo_a, repo_b = twin_projects( + tmp_path, context_file="AGENTS.md", context_markers=markers + ) + add_plan(repo_a) + add_plan(repo_b) + + bash = run_bash(repo_a) + py = run_python(repo_b) + + assert_parity(bash, py, repo_a, repo_b) + content = (repo_b / "AGENTS.md").read_bytes() + assert content == (repo_a / "AGENTS.md").read_bytes() + assert b"" in content + assert b"" in content + assert b"at specs/001-demo/plan.md" in content + + @requires_posix_bash def test_python_multiple_context_files_dedup_matching_bash(tmp_path: Path) -> None: files = ["AGENTS.md", "docs/CONTEXT.md", "AGENTS.md"]