Skip to content

Add skill invocation visibility for APM-restored skills - #49866

Merged
pelikhan merged 4 commits into
mainfrom
copilot/expose-skill-invocation-visibility
Aug 3, 2026
Merged

Add skill invocation visibility for APM-restored skills#49866
pelikhan merged 4 commits into
mainfrom
copilot/expose-skill-invocation-visibility

Conversation

Copilot AI commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

No first-class signal existed to confirm whether an APM-restored skill was actually invoked by the agent at runtime — only that it was installed. This closes that observability gap by parsing and surfacing skill invocation records through the full audit pipeline.

New type: SkillActivation

Added to logs_models.go with name, status ("invoked"), source ("agent_output" | "log_parse"), and standard ReportProvenance fields (run_id, workflow_name, timestamp, experiment/variant).

Extraction: extractSkillActivationsFromRun

Two-phase extraction in logs_metrics.go:

  1. Phase 1 — explicit: reads agent_output.json for items with "type": "skill_invocation". Agents opt into this by emitting through the safe-output mechanism.
  2. Phase 2 — log-parse fallback (skipped when Phase 1 yields results): walks raw agent log files for engine-specific patterns:

Phase 2 deduplicates by skill name across files.

Pipeline integration

  • SkillActivations wired into ProcessedRun, RunSummary, DownloadResult (including cache-hit restore), AuditData, and auditAnalysisResults
  • Extracted in both the logs pipeline (applyRunBehavioralSignals) and audit pipeline (launchCoreAuditAnalyses), with full experiment provenance
  • Persisted in run_summary.json
  • Rendered in gh aw audit console output:
    skill_activations:
      docs-check-style: invoked (source: agent_output)
      frontmatter-audit: invoked (source: log_parse)
    
  • Included in gh aw audit --json under skill_activations

Tests

pkg/cli/logs_skill_activations_test.go covers agent_output parsing, all three log-file patterns, deduplication, agent_output-over-log_parse precedence, and provenance field population.

- Add SkillActivation type with name, status, source, and provenance
- Add extractSkillActivationsFromRun: reads agent_output.json for
  explicit skill_invocation items, then falls back to scanning agent
  log files for Copilot's skill(name) pattern and structured log forms
- Wire into applyRunBehavioralSignals, finalizeAndSaveRunSummary,
  and the cache-hit DownloadResult reconstruction
- Add SkillActivations to auditAnalysisResults, ProcessedRun,
  RunSummary, DownloadResult, AuditData
- Render skill_activations section in gh aw audit console output
- Add extractSkillActivationsFromRun unit tests (agent_output, log
  parsing, precedence, and provenance verification)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Expose explicit skill invocation visibility for APM-restored skills Add skill invocation visibility for APM-restored skills Aug 2, 2026
Copilot AI requested a review from pelikhan August 2, 2026 22:45
@pelikhan

pelikhan commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

@copilot applies to all skills

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot applies to all skills

Done in 3e2cdf1. extractSkillActivationsFromRun now always runs both phases and merges their results, so every skill detected in agent logs is captured regardless of whether agent_output.json was also present. agent_output entries still win when the same skill name appears in both sources. Two tests were added/updated to cover the merged-sources case and the duplicate-deduplication case.

@pelikhan
pelikhan marked this pull request as ready for review August 2, 2026 23:15
Copilot AI review requested due to automatic review settings August 2, 2026 23:15
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ failed during design decision gate check.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

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 skill-invocation observability to the logs and audit pipelines.

Changes:

  • Introduces skill activation models and extraction.
  • Persists activations through run summaries and audit data.
  • Renders activations in console and JSON audit output.
Show a summary per file
File Description
pkg/cli/logs_skill_activations_test.go Tests activation extraction and provenance.
pkg/cli/logs_run_processor.go Integrates activations into run processing.
pkg/cli/logs_models.go Defines activation data structures.
pkg/cli/logs_metrics.go Extracts activations from output and logs.
pkg/cli/audit.go Integrates activations into audit analysis.
pkg/cli/audit_report.go Exposes activations in audit data.
pkg/cli/audit_report_render.go Renders activations in console output.

Review details

Tip

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

  • Files reviewed: 7/7 changed files
  • Comments generated: 5
  • Review effort level: Balanced

Comment thread pkg/cli/logs_metrics.go
// 3. `Skill invoked: <name>` — human-readable form (future/custom engines).
var skillInvocationPatterns = []*skillPattern{
{
re: regexp.MustCompile(`(?i)\bskill\(([A-Za-z0-9][A-Za-z0-9_.-]*)\)`),
Comment thread pkg/cli/logs_metrics.go
if err := json.Unmarshal(itemRaw, &item); err != nil {
continue
}
if item.Type != "skill_invocation" {
Comment thread pkg/cli/audit.go
MissingData: results.missingData,
Noops: results.noops,
MCPFailures: results.mcpFailures,
SkillActivations: results.skillActivations,
Comment thread pkg/cli/logs_metrics.go
agentOutputPath := filepath.Join(runDir, constants.AgentOutputArtifactName)
if stat, err := os.Stat(agentOutputPath); err == nil {
if stat.IsDir() {
nested := filepath.Join(agentOutputPath, constants.AgentOutputArtifactName)
Comment thread pkg/cli/logs_metrics.go
Comment on lines +827 to +830
// Phase 2 – scan raw agent log files for engine-specific patterns.
// Always runs so that skills not covered by agent_output.json are also captured.
// Skills already found in Phase 1 are skipped to avoid duplicates.
logActivations, logErr := extractSkillActivationsFromLogFiles(runDir, run, verbose, experimentName, variant)

@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: Add skill invocation visibility for APM-restored skills

The implementation is well-structured and consistent with existing audit pipeline patterns. Both extraction paths (agent_output.json and log file scanning) are clearly separated with correct precedence logic. Tests cover the core merging and deduplication scenarios thoroughly.

No blocking issues found. A few minor observations:

  • extractSkillActivationsFromLogFiles reads entire log files into memory with os.ReadFile; for very large log files this could be expensive, but the pattern is consistent with existing code in the file.
  • filepath.Walk in the log-file scanner returns partial activations alongside a non-nil walkErr. The caller treats this as a non-fatal warning (correct), so data is not lost.
  • The skillInvocationPatterns slice is package-level with an unexported skillPattern type — consistent with the rest of the file.

Overall the PR closes the observability gap clearly and safely.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 34.4 AIC · ⌖ 7.45 AIC · ⊞ 5.4K

@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 /codebase-design, /diagnosing-bugs, and /tdd — requesting changes on a few correctness and consistency issues.

📋 Key Themes & Highlights

Key Themes

  • Phase 2 contract mismatch: PR description says Phase 2 is skipped when Phase 1 yields results; the implementation (and the function's own doc comment) says Phase 2 always runs. One of them is wrong and needs fixing before merge.
  • Dead error check: extractSkillActivationsFromRun never returns a non-nil error, so the skillErr != nil guards in callers are unreachable — either surface errors or remove the guards.
  • Render inconsistency: renderConsoleSkillActivations uses raw string concatenation while every other render function uses console.Format* helpers or fmt.Sprintf.
  • Unguarded string literals: Status/Source values are magic strings; typed constants would prevent drift as new sources are added.
  • Shared test tmpDir: TestExtractSkillActivationsFromAgentOutput writes to the same path across table-driven sub-cases, which will break under t.Parallel().

Positive Highlights

  • ✅ Two-phase extraction with explicit source attribution is a clean, observable design
  • ✅ Tests cover all three log-file patterns, deduplication, precedence, and provenance fields
  • ✅ Pipeline integration (cached restore, audit pipeline, logs pipeline) is thorough and consistent

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

Comment thread pkg/cli/logs_metrics.go
))
}
logsMetricsLog.Printf("Found %d skill activation(s)", len(activations))
return activations, nil

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.

[/codebase-design] The PR description says Phase 2 is skipped when Phase 1 yields results, but the implementation always runs both phases and deduplicates by name. The doc comment on extractSkillActivationsFromRun also says "Phase 2 always runs to supplement", which contradicts the PR body. Please align the PR description or the implementation — if the contract is supplement, the code is correct but the PR body needs updating; if it is skip/fallback, the code over-reports log-parse entries alongside agent_output ones.

@copilot please address this.

result.MCPFailures = mcpFailures

skillActivations, skillErr := extractSkillActivationsFromRun(runOutputDir, result.Run, verbose, expName, expVariant)
if skillErr != nil && verbose {

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.

[/diagnosing-bugs] extractSkillActivationsFromRun always returns a nil error (it absorbs errors internally and logs via verbose). The skillErr != nil guard here is therefore dead code and will never fire. This is inconsistent with how other extractors in the same file are wired.

💡 Suggestion

Either surface real errors from extractSkillActivationsFromRun so callers can react, or remove the dead check and inline a comment noting that errors are already handled internally. The inconsistency will confuse the next reader.

@copilot please address this.

if len(activations) == 0 {
return
}
fmt.Fprintln(os.Stderr, " skill_activations:")

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.

[/codebase-design] renderConsoleSkillActivations builds lines with raw string concatenation (+ operator) while every other render function in this file uses a console.Format* helper or a format string. This is a style inconsistency that will make future colour/padding changes miss this section.

💡 Suggestion

Follow the pattern used by renderConsoleMissingTools and renderConsoleMCPFailures — either use fmt.Sprintf for the line or introduce a matching console.Format helper, whichever fits the output style of adjacent sections.

@copilot please address this.

Comment thread pkg/cli/logs_models.go
}

// SkillActivation records a detected skill invocation from agent logs.
// Source indicates where the invocation was detected: "agent_output" for

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.

[/codebase-design] Status and Source on SkillActivation are open strings with only informal constants in comments (// "invoked", // "agent_output" or "log_parse"). All other report types in this file use the same pattern, but these two fields are the most likely to diverge as new sources are added.

💡 Suggestion

Consider introducing typed constants (e.g. SkillActivationStatusInvoked, SkillActivationSourceAgentOutput, SkillActivationSourceLogParse) in the same file or logs_skill_activations.go. This makes future callsites self-documenting and prevents silent string drift.

@copilot please address this.

// TestExtractSkillActivationsFromLogFiles verifies that skill invocation patterns
// in raw agent log files are detected when no agent_output.json is present.
func TestExtractSkillActivationsFromLogFiles(t *testing.T) {
testRun := WorkflowRun{DatabaseID: 456, WorkflowName: "log-parse-test"}

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 test suite uses a shared tmpDir across all sub-cases in TestExtractSkillActivationsFromAgentOutput and uses t.Cleanup to remove the written file, but all sub-cases write to the same path (tmpDir/agent_output.json). If two sub-cases ever run concurrently (e.g. with t.Parallel()), they will clobber each other's files.

💡 Suggestion

Move tmpDir := testutil.TempDir(t, ...) inside each sub-case's t.Run closure (like the TestExtractSkillActivationsFromLogFiles test already does) so each sub-case gets an isolated directory.

@copilot please address this.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 95/100 — Excellent

Analyzed 5 test function(s) (14 table-driven scenarios): 5 design, 0 implementation, 0 violation(s).

📊 Metrics (5 tests, 14 scenarios)
Metric Value
Analyzed 5 (Go: 5, JS: 0)
✅ Design 5 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 5 (100%)
Duplicate clusters 0
Inflation N/A (new file pair added together)
🚨 Violations 0
Test File Classification Issues
TestExtractSkillActivationsFromAgentOutput pkg/cli/logs_skill_activations_test.go design_test / behavioral_contract None
TestExtractSkillActivationsFromLogFiles pkg/cli/logs_skill_activations_test.go design_test / behavioral_contract None
TestExtractSkillActivationsBothSourcesMerged pkg/cli/logs_skill_activations_test.go design_test / behavioral_contract None
TestExtractSkillActivationsAgentOutputWinsOnDuplicate pkg/cli/logs_skill_activations_test.go design_test / behavioral_contract None
TestExtractSkillActivationsProvenanceFields pkg/cli/logs_skill_activations_test.go design_test / behavioral_contract None

Highlights

  • ✅ Build tag //go:build !integration on line 1 — no violation.
  • ✅ No mock libraries (gomock, testify/mock) — uses real filesystem with testutil.TempDir.
  • ✅ Table-driven tests with well-named scenarios (5 in agent-output suite, 6 in log-parse suite).
  • ✅ Edge cases: empty skill name skipped, missing status defaults to invoked, deduplication across sources, agent_output priority over log parse, case-insensitive log matching.
  • TestExtractSkillActivationsAgentOutputWinsOnDuplicate encodes a design invariant (source priority rule).
  • TestExtractSkillActivationsProvenanceFields verifies all ReportProvenance fields propagated correctly.
  • ✅ Descriptive t.Errorf / t.Fatalf messages throughout.

Verdict

passed. 0% implementation tests (threshold: 30%). All 5 tests enforce behavioral contracts with solid edge-case coverage.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 · 58.7 AIC · ⌖ 11.3 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: 95/100. 0% implementation tests (threshold: 30%). All 5 tests enforce behavioral contracts with solid edge-case coverage.

Captures the architectural decision to surface SkillActivation records
via dual-source extraction (agent_output.json + log-file pattern scan)
as required by the design decision gate for PR #49866.
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the remaining blockers on this PR, then run the pr-finisher skill.

Outstanding review items (newest first):

  • github-actions: fix the Phase 2 contract mismatch between the PR description and implementation/doc comment.
  • github-actions: remove or justify the dead skillErr != nil guards since extractSkillActivationsFromRun never returns a non-nil error.
  • github-actions: align renderConsoleSkillActivations with the existing console.Format* / fmt.Sprintf rendering pattern.
  • github-actions: replace raw Status / Source magic strings with typed constants to avoid drift.
  • github-actions: isolate per-subtest temp paths in TestExtractSkillActivationsFromAgentOutput so it is safe under t.Parallel().
  • Failed check to revisit: PR Code Quality Reviewerhttps://github.com/github/gh-aw/actions/runs/30771862234

Branch refresh may help once you are ready.

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

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose explicit skill invocation visibility for APM-restored skills

4 participants