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{