Skip to content

Add unit tests for compiler_yaml_prompt.go prompt-chunking logic - #49972

Merged
pelikhan merged 7 commits into
mainfrom
copilot/add-missing-unit-test-file
Aug 3, 2026
Merged

Add unit tests for compiler_yaml_prompt.go prompt-chunking logic#49972
pelikhan merged 7 commits into
mainfrom
copilot/add-missing-unit-test-file

Conversation

Copilot AI commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

compiler_yaml_prompt.go (336 lines, 11 functions) had no dedicated test file despite containing non-trivial prompt-chunking, import-interleaving, and expression-mapping logic that relies entirely on indirect integration coverage.

Added: pkg/workflow/compiler_yaml_prompt_test.go

Focused unit tests for all key functions:

Test Function(s)
TestProcessOrderedPromptImports + fallback processOrderedPromptImports — inline markdown, runtime-import macros, mixed entries, import inputs, missing-file fallback
TestProcessLegacyPromptImports + inlined/fallback variants processLegacyPromptImports — all import modes including disk-read and fallback
TestEnrichExpressionMappings inline vs non-inline expression extraction, experiment mappings, existing-mapping preservation
TestBuildMainWorkflowPromptChunks + expression extraction inline embed vs runtime-import macro selection, path resolution variants
TestProcessPromptImportEntriesDispatch ordered vs legacy path routing in processPromptImportEntries
TestMergeKnownNeedsExpressions merge precedence — all-entries win over knownNeeds for same env var
TestResolveWorkspaceRoot .github/-relative and fallback path variants
TestExtractPromptChunksFromMarkdown XML comment stripping, expression extraction, chunking pipeline

splitContentIntoChunks already has dedicated coverage in xml_comments_test.go; tests here exercise it indirectly through the pipeline.


run: https://github.com/github/gh-aw/actions/runs/30815680828

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.88 AIC · ⌖ 4.97 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Copilot AI and others added 2 commits August 3, 2026 12:32
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add missing unit test file for compiler_yaml_prompt.go Add unit tests for compiler_yaml_prompt.go prompt-chunking logic Aug 3, 2026
Copilot AI requested a review from pelikhan August 3, 2026 12:41
@pelikhan
pelikhan marked this pull request as ready for review August 3, 2026 12:47
Copilot AI review requested due to automatic review settings August 3, 2026 12:47
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds dedicated unit coverage for prompt chunking, imports, expression mappings, and path resolution.

Changes:

  • Adds focused compiler prompt tests.
  • Extends the agentic-workflows reference catalog.
Show a summary per file
File Description
pkg/workflow/compiler_yaml_prompt_test.go Adds prompt-processing unit tests.
.github/skills/agentic-workflows/SKILL.md Lists the observability optimization guide.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Suppressed comments (2)

pkg/workflow/compiler_yaml_prompt_test.go:162

  • This substitution case is also vacuous: the input omits the required $ in ${{ ... }}, and the row has no output expectation. It therefore passes without testing the legacy substitution path.
			name:             "inline markdown with import inputs substitution",
			importedMarkdown: "Role: {{ github.aw.inputs.role }}",
			importInputs:     map[string]any{"role": "engineer"},
		},

pkg/workflow/compiler_yaml_prompt_test.go:108

  • Only the read-error fallback is exercised for ordered imports with InlinedImports enabled. Add the corresponding successful-read case and verify that the imported body and its expression mappings are returned instead of a runtime-import macro; otherwise the ordered compile-time inlining branch remains unprotected.
func TestProcessOrderedPromptImportsFileReadFallback(t *testing.T) {
  • Files reviewed: 2/2 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment on lines +48 to +54
{
name: "markdown with import inputs substitution",
promptImports: []parser.PromptImportEntry{
{Markdown: "Hello {{ github.aw.inputs.name }}!"},
},
importInputs: map[string]any{"name": "World"},
},

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
chunks, _ := extractPromptChunksFromMarkdown(tt.body)
{Markdown: "# Inline Content"},
{ImportPath: ".github/workflows/extra.md"},
},
wantChunkContain: []string{"# Inline Content", "{{#runtime-import .github/workflows/extra.md}}"},
Comment on lines +343 to +345
if len(result) < tt.wantMinMappings {
t.Errorf("enrichExpressionMappings() got %d mappings, want at least %d", len(result), tt.wantMinMappings)
}
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 86/100 — Excellent

Analyzed 12 test(s): 10 design, 2 implementation, 0 violation(s).

📊 Metrics (12 tests)
Metric Value
Analyzed 12 (Go: 12, JS: 0)
✅ Design 10 (83%)
⚠️ Implementation 2 (17%)
Edge/error coverage 9 (75%)
Duplicate clusters 0
Inflation N/A (production file not modified)
🚨 Violations 0
Test File Classification Issues
TestProcessOrderedPromptImports compiler_yaml_prompt_test.go:15 design_test 1 no-op subcase (substitution case has no wantChunkContain)
TestProcessOrderedPromptImportsFileReadFallback compiler_yaml_prompt_test.go:108 design_test None
TestProcessLegacyPromptImports compiler_yaml_prompt_test.go:138 design_test 1 no-op subcase (substitution case has no wantChunkContain)
TestProcessLegacyPromptImportsInlinedFromDisk compiler_yaml_prompt_test.go:204 design_test None
TestProcessLegacyPromptImportsInlinedFallback compiler_yaml_prompt_test.go:241 design_test None
TestEnrichExpressionMappings compiler_yaml_prompt_test.go:266 design_test None
TestBuildMainWorkflowPromptChunks compiler_yaml_prompt_test.go:363 design_test None
TestBuildMainWorkflowPromptChunksExpressionExtraction compiler_yaml_prompt_test.go:471 design_test None
TestProcessPromptImportEntriesDispatch compiler_yaml_prompt_test.go:489 implementation_test Tests dispatch routing rather than a user-visible behavioral contract
TestMergeKnownNeedsExpressions compiler_yaml_prompt_test.go:522 design_test None
TestResolveWorkspaceRoot compiler_yaml_prompt_test.go:602 implementation_test Tests a private helper; low behavioral value
TestExtractPromptChunksFromMarkdown compiler_yaml_prompt_test.go:641 design_test None
⚠️ Flagged Tests (2)

TestProcessOrderedPromptImports — subcase "markdown with import inputs substitution" (compiler_yaml_prompt_test.go:49) — The subcase sets importInputs: {"name": "World"} but specifies no wantChunkContain, so it exercises the code path without asserting the substitution actually occurred. Add wantChunkContain: []string{"Hello World!"} to verify the template variable is resolved.

TestProcessLegacyPromptImports — subcase "inline markdown with import inputs substitution" (compiler_yaml_prompt_test.go:159) — Same pattern: importInputs: {"role": "engineer"} is set but no assertion checks the resolved content. Add wantChunkContain: []string{"Role: engineer"} to make this subcase meaningful.

Verdict

passed. 17% implementation tests (threshold: 30%). No violations. Two no-op substitution subcases (flagged above) reduce confidence in template-input coverage but are not blocking.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 · 41.6 AIC · ⌖ 11.2 AIC · ⊞ 8.4K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 86/100. 17% implementation tests (threshold: 30%). No violations.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Good coverage addition — the test suite is well-structured and exercises all the key functions. Two blocking issues need fixing before merge:

1. Error returns silently discarded (all test functions)
Every chunks, _ := ... call silently swallows errors. If a function returns an error under an unexpected condition the test passes with wrong results. All error returns should be checked with t.Fatalf.

2. Fragile wantChunkCount == 0 assertion guard
if tt.wantChunkCount > 0 && ... means the assertion is dead code when the field is 0 (Go zero-value). Test cases that explicitly expect 0 chunks (wantChunkCount: 0) are never verified. Use -1 as the "unchecked" sentinel and guard with >= 0, or use a *int pointer field.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 39.8 AIC · ⌖ 7.62 AIC · ⊞ 5.4K

}
if !found {
t.Errorf("processOrderedPromptImports() expected chunks to contain %q; got: %v", sub, chunks)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error returns are silently discarded throughout the test file.

Nearly every call uses chunks, _ := ..., so if a function returns an unexpected error the test silently passes with empty/wrong results instead of failing loudly.

Consider checking errors explicitly:

chunks, err := c.processOrderedPromptImports(data)
if err != nil {
    t.Fatalf("unexpected error: %v", err)
}

This pattern applies to all usages at lines ~100, ~146, ~203, ~247, ~274, ~456, ~515, ~529, and ~693.

@copilot please address this.

t.Errorf("processOrderedPromptImports() expected chunks to contain %q; got: %v", sub, chunks)
}
}
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragile wantChunkCount == 0 guard causes silent test misses.

The assertion:

if tt.wantChunkCount > 0 && len(chunks) != tt.wantChunkCount {

skips the assertion entirely when wantChunkCount is 0 (the zero-value default). This means test cases that expect exactly 0 chunks are never verified unless additional guard conditions are met. The same pattern recurs at line ~458.

Prefer an explicit sentinel so intent is unambiguous:

type optionalInt struct{ set bool; val int }

or simply use -1 as "not set" and check >= 0:

if tt.wantChunkCount >= 0 && len(chunks) != tt.wantChunkCount {
    t.Errorf(...)
}

(initialise the field to -1 in cases where you don't care about the count)

@copilot please address this.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate - ADR Required

This PR makes significant changes to core business logic (693 new lines in pkg/workflow/) but does not have a linked Architecture Decision Record (ADR).

Draft ADR committed: docs/adr/49972-add-unit-tests-for-compiler-yaml-prompt.md - review and complete it before merging.

This PR cannot merge until an ADR is linked in the PR body.

What to do next
  1. Review the draft ADR committed to your branch - it was generated from the PR diff
  2. Complete the missing sections - add context the AI could not infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:
    ADR: [ADR-49972: Add Dedicated Unit Tests for compiler_yaml_prompt.go](docs/adr/49972-add-unit-tests-for-compiler-yaml-prompt.md)

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context - What is the problem? What forces are at play?
  • Decision - What did you decide? Why?
  • Alternatives Considered - What else could have been done?
  • Consequences - What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 0042-use-postgresql.md for PR #42).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 66.4 AIC · ⌖ 28.5 AIC · ⊞ 9.6K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /tdd — requesting changes on assertion quality gaps.

📋 Key Themes & Highlights

Key Themes

  • Error returns ignored: All chunks, _ := ... calls discard errors, hiding real failures. Affects every test function.
  • Ambiguous zero-count sentinel: wantChunkCount > 0 guard makes wantChunkCount: 0 unenforceable without a secondary condition — this is confusing and fragile.
  • Substitution not asserted: Two test cases exercise importInputs substitution but never verify the output contains the substituted value.
  • Expression mapping content unchecked: TestBuildMainWorkflowPromptChunksExpressionExtraction only checks len > 0, not what the mappings contain.

Positive Highlights

  • ✅ Excellent breadth — all 11 functions now have dedicated unit coverage
  • ✅ Good use of t.TempDir() for filesystem-dependent tests
  • ✅ Fallback behaviour (runtime-import macro on missing file) is explicitly tested
  • ✅ Table-driven test structure is clear and consistent
  • ✅ Build tag //go:build !integration is correctly applied

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 48 AIC · ⌖ 11.3 AIC · ⊞ 7.1K
Comment /matt to run again

}
if !found {
t.Errorf("processOrderedPromptImports() expected chunks to contain %q; got: %v", sub, chunks)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] Error return is silently discarded — if processOrderedPromptImports returns a non-nil error, the test will not catch it and may assert on garbage chunks.

💡 Fix

Change the call to:

chunks, err := c.processOrderedPromptImports(data)
if err != nil {
    t.Fatalf("unexpected error: %v", err)
}

This pattern recurs on lines 146, 203, 247, 274, 456, 515, 529, and 693 — all error returns should be asserted nil.

@copilot please address this.

t.Errorf("processOrderedPromptImports() expected chunks to contain %q; got: %v", sub, chunks)
}
}
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] wantChunkCount == 0 is used ambiguously — the guard if tt.wantChunkCount > 0 skips the count assertion entirely when the expected count is 0, meaning a test case that sets wantChunkCount: 0 never actually asserts zero chunks unless the imports list is also empty.

💡 Suggested fix

Replace the two-condition check with a single consistent pattern:

if tt.wantChunkCount >= 0 {
    if len(chunks) != tt.wantChunkCount {
        t.Errorf("got %d chunks, want %d", len(chunks), tt.wantChunkCount)
    }
}

Or initialise wantChunkCount to -1 as the sentinel "do not check" value so that 0 reliably means "assert empty".

The same pattern appears in TestBuildMainWorkflowPromptChunks around line 458.

@copilot please address this.

promptImports: []parser.PromptImportEntry{},
wantChunkCount: 0,
},
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The "markdown with import inputs substitution" case (line 60) sets importInputs and wantChunkContain is left nil — the test exercises the substitution code path but never asserts the substituted value was used. If the substitution silently fails, this test still passes.

💡 Fix

Add an assertion:

wantChunkContain: []string{"Hello World!"},

Similarly, the "inline markdown with import inputs substitution" case in TestProcessLegacyPromptImports (around line 177) has the same gap.

@copilot please address this.

chunks, _ := c.processPromptImportEntries(data)
if len(chunks) == 0 {
t.Error("processPromptImportEntries() expected chunks from ordered path")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] TestBuildMainWorkflowPromptChunksExpressionExtraction only asserts len(mappings) == 0 but never checks the content of the returned mappings. A bug that returns a non-empty slice of zero-value mappings would pass undetected.

💡 Fix

Add a content assertion:

if mappings[0].Content != "github.event.issue.number" {
    t.Errorf("expected expression content %q, got %q", "github.event.issue.number", mappings[0].Content)
}

@copilot please address this.

}{
{
name: "empty body",
body: "",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] TestExtractPromptChunksFromMarkdown has an empty-body case that asserts wantChunks: 1 but leaves wantChunkContain empty — so it only checks that exactly one chunk is returned without verifying the chunk content is the empty string (or at least not nil). This makes the test brittle: any non-empty string accidentally returned would still pass.

💡 Fix

Either assert the single chunk is an empty string:

wantChunkContain: []string{""},

Or document intentionally that chunk content is unchecked for the empty-body case with an inline comment.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot sous-chef triage:

Please refresh the branch if needed and then run the pr-finisher skill.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.88 AIC · ⌖ 4.97 AIC · ⊞ 8.3K ·
Comment /souschef to run again

- Use ${{ github.aw.inputs.* }} form in import-inputs substitution cases
  and assert the substituted output (Hello World!, Role: engineer)
- Add wantChunkSequence field to ordered/legacy import tests to assert
  exact chunk interleaving order, not just "appears somewhere"
- Replace wantMinMappings with exact wantMappingCount in
  TestEnrichExpressionMappings so zero-mapping cases can fail
- TestExtractPromptChunksFromMarkdown: capture mappings, assert
  XML-comment strings are absent from output, and assert raw ${{ }}
  expressions are replaced by env-var placeholders in chunks

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@copilot sous-chef triage:

Please refresh the branch if needed and then run the pr-finisher skill....

Done. Addressed all four reviewer items in commit test(compiler_yaml_prompt): address all four review items:

  • Import-inputs substitution (lines 48-54, 159): switched to the correct ${{ github.aw.inputs.* }} form and added wantChunkContain assertions that verify the substituted value appears in the output ("Hello World!", "Role: engineer").
  • Exact interleaving order (lines 46, 108): added a wantChunkSequence []string field to both ordered- and legacy-import table tests; the mixed-entries case now asserts chunks in index order (inline chunk first, runtime-import macro second).
  • Exact count in TestEnrichExpressionMappings (lines 343-345): renamed wantMinMappingswantMappingCount and changed the guard from < to !=, so the "empty state" and "inline mode skips extraction" rows can now actually fail.
  • TestExtractPromptChunksFromMarkdown (line 675): captures the returned mappings; the XML-comment case now asserts "hidden" is absent from all chunks; the expression case asserts exactly 1 mapping is returned and that the raw ${{ github.event.issue.number }} is absent from the chunks (replaced by its env-var placeholder).

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Triage

Category: test · Risk: low · Total score: 43/100
Batch: batch-quality-1 (grouped with #49962, #49971 — low-risk quality/tooling PRs)

Impact Urgency Quality
18/50 8/30 17/20

Adds a dedicated unit test file for compiler_yaml_prompt.go prompt-chunking logic — no production code changes.

Recommended action: batch_review — test-only addition, safe to batch-review with related quality PRs.

Generated by 🔧 PR Triage Agent · auto · 55.7 AIC · ⌖ 4.24 AIC · ⊞ 8K ·

@pelikhan
pelikhan merged commit 3f474bc into main Aug 3, 2026
27 of 29 checks passed
@pelikhan
pelikhan deleted the copilot/add-missing-unit-test-file branch August 3, 2026 14:44
@github-actions github-actions Bot mentioned this pull request Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Code Quality] Add missing unit test file for compiler_yaml_prompt.go prompt-chunking logic

4 participants