From 7d63c6b7e4a0b9c6138af5d68e81d23815857619 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:31:48 +0000 Subject: [PATCH 1/2] Initial plan From 2e2045f61e1bd5652046ea254f2a032072bf6978 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:25:40 +0000 Subject: [PATCH 2/2] Fix: __GH_AW_WIKI_NOTE__ placeholder not substituted when Wiki is disabled - Add fallback substitution in validate_prompt_placeholders.sh for backwards compatibility - Add test verifying GH_AW_WIKI_NOTE is always in the substitution step for both wiki/non-wiki mode - Add changeset documenting the fix Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .changeset/patch-fix-wiki-note-placeholder.md | 4 ++ .../setup/sh/validate_prompt_placeholders.sh | 12 ++++ .../sh/validate_prompt_placeholders_test.sh | 56 +++++++++++++++ pkg/workflow/unified_prompt_creation_test.go | 70 +++++++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 .changeset/patch-fix-wiki-note-placeholder.md diff --git a/.changeset/patch-fix-wiki-note-placeholder.md b/.changeset/patch-fix-wiki-note-placeholder.md new file mode 100644 index 00000000000..d6cfb3f3bf8 --- /dev/null +++ b/.changeset/patch-fix-wiki-note-placeholder.md @@ -0,0 +1,4 @@ +--- +"gh-aw": patch +--- +Fix `__GH_AW_WIKI_NOTE__` placeholder not being substituted when repo has no GitHub Wiki enabled. The activation job now adds `GH_AW_WIKI_NOTE` to the placeholder substitution step even when wiki mode is disabled (empty string). The `validate_prompt_placeholders.sh` script also applies a fallback substitution for this placeholder with a warning, ensuring backwards compatibility with workflows compiled before this fix. diff --git a/actions/setup/sh/validate_prompt_placeholders.sh b/actions/setup/sh/validate_prompt_placeholders.sh index 847e2b07a8b..20e0db4b4ac 100755 --- a/actions/setup/sh/validate_prompt_placeholders.sh +++ b/actions/setup/sh/validate_prompt_placeholders.sh @@ -13,6 +13,18 @@ fi echo "🔍 Validating prompt placeholders..." +# Fallback: substitute __GH_AW_WIKI_NOTE__ with an empty string if still present. +# This handles workflows compiled before GH_AW_WIKI_NOTE was added to the substitution +# step for non-wiki repos. The wiki note is always optional (empty = no wiki note), +# so it is safe to clear it here rather than failing with a confusing placeholder error. +# If you see this message, run `gh aw update` to recompile your workflow and avoid the fallback. +if grep -q "__GH_AW_WIKI_NOTE__" "$PROMPT_FILE"; then + echo "⚠️ Warning: __GH_AW_WIKI_NOTE__ was not substituted by the substitution step." + echo " Applying fallback: replacing with empty string." + echo " To resolve this permanently, run 'gh aw update' to recompile your workflow." + sed 's/__GH_AW_WIKI_NOTE__//g' "$PROMPT_FILE" > "$PROMPT_FILE.tmp" && mv "$PROMPT_FILE.tmp" "$PROMPT_FILE" +fi + # Check for unreplaced environment variable placeholders (format: __GH_AW_*__) if grep -q "__GH_AW_" "$PROMPT_FILE"; then echo "❌ Error: Found unreplaced placeholders in prompt file:" diff --git a/actions/setup/sh/validate_prompt_placeholders_test.sh b/actions/setup/sh/validate_prompt_placeholders_test.sh index 5e74405d97a..98c0fd22798 100755 --- a/actions/setup/sh/validate_prompt_placeholders_test.sh +++ b/actions/setup/sh/validate_prompt_placeholders_test.sh @@ -95,3 +95,59 @@ fi echo "" echo "🎉 All validation tests passed!" + +# Test 5: Prompt with __GH_AW_WIKI_NOTE__ placeholder (fallback should apply, not fail) +echo "Test 5: Prompt with unsubstituted __GH_AW_WIKI_NOTE__ (fallback should apply)" +cat > "$TEST_DIR/prompt_wiki_note.txt" << 'EOF' + +# Repo Memory +You have access to a persistent repo memory folder at `/tmp/gh-aw/repo-memory/default/` +where you can read and write files that are stored in a git branch.__GH_AW_WIKI_NOTE__ + + +# User Task +Do something useful. +EOF + +export GH_AW_PROMPT="$TEST_DIR/prompt_wiki_note.txt" +OUTPUT=$(bash "$SCRIPT_PATH" 2>&1) +STATUS=$? +if [ $STATUS -eq 0 ]; then + echo "✅ Test 5 passed: Prompt with __GH_AW_WIKI_NOTE__ handled by fallback" + if echo "$OUTPUT" | grep -q "Warning.*__GH_AW_WIKI_NOTE__"; then + echo " (fallback warning message shown as expected)" + fi + # Verify __GH_AW_WIKI_NOTE__ was actually removed from the file + if grep -q "__GH_AW_WIKI_NOTE__" "$TEST_DIR/prompt_wiki_note.txt"; then + echo "❌ Test 5 failed: __GH_AW_WIKI_NOTE__ was not removed by fallback" + exit 1 + fi +else + echo "❌ Test 5 failed: Prompt with __GH_AW_WIKI_NOTE__ caused unexpected failure" + echo "Output: $OUTPUT" + exit 1 +fi +echo "" + +# Test 6: Prompt with OTHER unreplaced placeholder (should still fail) +echo "Test 6: Prompt with other unreplaced placeholder (should still fail)" +cat > "$TEST_DIR/prompt_other_placeholder.txt" << 'EOF' + +# System Instructions +Memory directory: __GH_AW_MEMORY_DIR__ + + +# User Task +Do something useful. +EOF + +export GH_AW_PROMPT="$TEST_DIR/prompt_other_placeholder.txt" +if bash "$SCRIPT_PATH" 2>&1; then + echo "❌ Test 6 failed: Prompt with __GH_AW_MEMORY_DIR__ was accepted" + exit 1 +else + echo "✅ Test 6 passed: Prompt with other unreplaced placeholder rejected" +fi +echo "" + +echo "🎉 All validation tests passed!" diff --git a/pkg/workflow/unified_prompt_creation_test.go b/pkg/workflow/unified_prompt_creation_test.go index cfbecc282bf..71efd314b7b 100644 --- a/pkg/workflow/unified_prompt_creation_test.go +++ b/pkg/workflow/unified_prompt_creation_test.go @@ -561,6 +561,76 @@ func TestGenerateUnifiedPromptCreationStep_CacheAndRepoMemory(t *testing.T) { assert.Less(t, systemClosePos, userPos, "User task should be after system tag closes") } +// TestGenerateUnifiedPromptCreationStep_WikiNoteInSubstitution verifies that GH_AW_WIKI_NOTE +// is always present in the placeholder substitution step for repo-memory, regardless of wiki mode. +// Regression test for: __GH_AW_WIKI_NOTE__ placeholder not substituted when Wiki is disabled. +func TestGenerateUnifiedPromptCreationStep_WikiNoteInSubstitution(t *testing.T) { + tests := []struct { + name string + wiki bool + expectEmpty bool + expectContain string + }{ + { + name: "non-wiki mode has empty GH_AW_WIKI_NOTE in substitution", + wiki: false, + expectEmpty: true, + }, + { + name: "wiki mode has non-empty GH_AW_WIKI_NOTE in substitution", + wiki: true, + expectEmpty: false, + expectContain: "GitHub Wiki", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + compiler := &Compiler{} + data := &WorkflowData{ + ParsedTools: NewTools(map[string]any{}), + RepoMemoryConfig: &RepoMemoryConfig{ + Memories: []RepoMemoryEntry{ + {ID: "default", BranchName: "memory/default", Wiki: tt.wiki}, + }, + }, + } + + builtinSections := compiler.collectPromptSections(data) + userPromptChunks := []string{"# User Task"} + + var yamlBuf strings.Builder + allExpressionMappings := compiler.generateUnifiedPromptCreationStep(&yamlBuf, builtinSections, userPromptChunks, nil, data) + + require.NotEmpty(t, allExpressionMappings, "Expected non-empty expression mappings") + + // Verify GH_AW_WIKI_NOTE is always in the substitution mappings + var wikiNoteMapping *ExpressionMapping + for _, m := range allExpressionMappings { + if m.EnvVar == "GH_AW_WIKI_NOTE" { + wikiNoteMapping = m + break + } + } + require.NotNil(t, wikiNoteMapping, "GH_AW_WIKI_NOTE must be present in substitution mappings (required to substitute __GH_AW_WIKI_NOTE__ in repo_memory_prompt.md)") + + // Verify the substitution step YAML contains the env var + var substYaml strings.Builder + generatePlaceholderSubstitutionStep(&substYaml, allExpressionMappings, " ") + substOutput := substYaml.String() + + assert.Contains(t, substOutput, "GH_AW_WIKI_NOTE:", "Substitution step must declare GH_AW_WIKI_NOTE env var") + assert.Contains(t, substOutput, "GH_AW_WIKI_NOTE: process.env.GH_AW_WIKI_NOTE", "Substitution step must pass GH_AW_WIKI_NOTE to substitutePlaceholders") + + if tt.expectEmpty { + assert.Contains(t, substOutput, "GH_AW_WIKI_NOTE: ''", "Non-wiki mode should have empty GH_AW_WIKI_NOTE env var") + } else { + assert.Contains(t, substOutput, tt.expectContain, "Wiki mode should have wiki note content in GH_AW_WIKI_NOTE env var") + } + }) + } +} + // TestGenerateUnifiedPromptCreationStep_PRContextConditional tests that PR context uses shell conditions func TestGenerateUnifiedPromptCreationStep_PRContextConditional(t *testing.T) { compiler := &Compiler{