Skip to content

Add steering message rendering in unified log view#38277

Merged
pelikhan merged 6 commits into
mainfrom
copilot/add-rendering-of-steering-messages
Jun 10, 2026
Merged

Add steering message rendering in unified log view#38277
pelikhan merged 6 commits into
mainfrom
copilot/add-rendering-of-steering-messages

Conversation

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

AWF API proxy steering events (token_steering, timeout_steering) were being counted in TotalSteeringEvents metrics but silently dropped from the gh aw view / gh aw audit unified timeline.

Changes

token_usage.go

  • proxyEventsEntry struct (handles all four event-name field variants: event, type, event_name, eventName) and its eventName() method moved here from gateway_logs_timeline.go so both files share a single typed decoder for api-proxy-logs/events.jsonl
  • parseAPIProxySteeringEvents updated to use the typed struct instead of map[string]any, eliminating the duplicate parsing path

gateway_logs_timeline.go

  • Adds TimelineKindSteering event kind
  • steeringEntryToTimelineEvent validates against spec message prefixes ([AWF TOKEN WARNING] / [AWF TIME WARNING]); sets Status to "token" or "time"; parses optional RFC3339 timestamp
  • collectSteeringTimelineEvents reads from api-proxy-logs/events.jsonl (both canonical sandbox/firewall/logs/ and legacy firewall-audit-logs/ layouts)
  • BuildUnifiedTimeline now includes steering events

gateway_logs_timeline_render.go

  • icon, "steering" label, renderSteeringRow for table renderer
  • Stream renderer emits a warning-styled ⚠ <message> line at 4-space indent (consistent with all other firewall-sourced events)
  • renderUnifiedTimeline summary appends steering=N to the Firewall line when non-zero; counting loop restructured so firewall-specific counters are scoped under TimelineSourceFirewall

Compiled workflow lockfiles (*.lock.yml)

  • All workflow lockfiles regenerated via make recompile after merging main

Tests

  • 15 new tests covering all field-name variants, with/without timestamps, wrong-prefix rejection, collection from both directory layouts, and table/stream rendering

Copilot AI and others added 2 commits June 10, 2026 04:00
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI requested a review from pelikhan June 10, 2026 04:11
@pelikhan pelikhan marked this pull request as ready for review June 10, 2026 04:11
Copilot AI review requested due to automatic review settings June 10, 2026 04:11

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

This PR extends the gh aw view / gh aw audit unified timeline to include AWF API proxy steering events (token/time budget warnings) so they are visible in both table and streaming renderers, and adds test coverage for the new parsing + rendering paths. It also updates a large set of compiled workflow lockfiles to emit new “detect agent errors” outputs/env vars, expanding the PR scope beyond what the description currently states.

Changes:

  • Add parsing/collection of token_steering and timeout_steering from api-proxy-logs/events.jsonl and include them in BuildUnifiedTimeline.
  • Render steering events in both table and stream unified timeline outputs (icon/label + summary count).
  • Add extensive unit tests for steering event parsing/collection and rendering; update many .lock.yml workflows to add a “Detect agent errors” step + outputs wiring.
Show a summary per file
File Description
pkg/cli/gateway_logs_timeline.go Adds steering event kind and collects/parses proxy events.jsonl into unified timeline events.
pkg/cli/gateway_logs_timeline_render.go Adds steering icon/label, table/stream rendering, and includes steering counts in the unified summary.
pkg/cli/gateway_logs_timeline_test.go Adds tests for steering parsing, collection, unified inclusion, and both renderers.
.github/workflows/unbloat-docs.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/typist.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/test-create-pr-error-handling.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/step-name-alignment.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/static-analysis-report.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/spec-enforcer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-codex.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-claude.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-call-workflow.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-agent-scoped-approved.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-agent-public-none.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-agent-public-approved.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-agent-all-none.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/smoke-agent-all-merged.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/sergo.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/semantic-function-refactor.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/scout.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/schema-feature-coverage.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/schema-consistency-checker.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/safe-output-health.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/ruflo-backed-task.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/prompt-clustering-analysis.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/necromancer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/lockfile-stats.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/issue-arborist.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/instructions-janitor.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/hourly-ci-cleaner.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/grumpy-reviewer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/go-pattern-detector.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/go-logger.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/go-fan.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/github-mcp-tools-report.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/github-mcp-structural-analysis.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/example-workflow-analyzer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/duplicate-code-detector.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/developer-docs-consolidator.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/dev.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/design-decision-gate.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/deep-report.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-token-consumption-report.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-team-evolution-insights.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-security-red-team.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-safeoutputs-git-simulator.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-safe-outputs-conformance.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-safe-output-optimizer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-rendering-scripts-verifier.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-reliability-review.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-observability-report.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-multi-device-docs-tester.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-function-namer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-fact.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-doc-updater.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-doc-healer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-code-metrics.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-choice-test.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-caveman-optimizer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-cache-strategy-analyzer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-aw-cross-repo-compile-check.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/daily-agentrx-trace-optimizer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/copilot-session-insights.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/copilot-agent-analysis.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/commit-changes-analyzer.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/codex-github-remote-mcp-test.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/cloclo.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/cli-version-checker.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/claude-code-user-docs-review.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/ci-doctor.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/changeset.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/blog-auditor.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/aw-failure-investigator.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/avenger.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/audit-workflows.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/approach-validator.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/api-consumption-report.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.
.github/workflows/ai-moderator.lock.yml Adds “Detect agent errors” step and wires new error-related outputs/env vars.

Copilot's findings

Tip

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

Comments suppressed due to low confidence (1)

pkg/cli/gateway_logs_timeline.go:672

  • BuildUnifiedTimeline sorts events purely by Time, but steering (and potentially other) events can legitimately have a zero timestamp. Since time.Time{} compares as the year 1, those events will always sort to the very start of the unified timeline, which makes the rendered chronology misleading (they show up with "-" time at the top). Consider treating zero timestamps as "unknown" and sorting them after timestamped events, and use a stable sort so equal/unknown timestamps preserve append order.
  • Files reviewed: 80/80 changed files
  • Comments generated: 1

GH_AW_PROJECT_UTC: "-08:00"
GH_AW_WORKFLOW_ID_SANITIZED: unbloatdocs
outputs:
agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer was skipped during the skills-based review.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel was skipped during test quality analysis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (475 new lines in pkg/cli/, above the 100-line threshold) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/38277-render-awf-steering-events-in-unified-timeline.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 confirm the alternatives reflect what you actually 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-38277: Render AWF Steering Events in the Unified Log Timeline

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 — for example, why steering events reuse the firewall source and overload the Status field rather than introducing a new timeline source. 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., 38277-...md for PR #38277).

🔒 Blocking: link the completed ADR in the PR body to clear this gate.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · 91.3 AIC · ⌖ 9.91 AIC ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@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.

REQUEST_CHANGES — 1 rendering bug + 2 maintainability issues

The steering-events feature is well-structured overall (good typed struct, solid test coverage), but three issues need to be addressed before merge.

Blocking issues (3)
  1. Stream indentation bug (gateway_logs_timeline_render.go:607) — Steering events render with 2-space indent inside renderUnifiedTimelineStream, while every other firewall-sourced event (NetworkAllowed, NetworkBlocked, DIFCFiltered, GuardPolicyBlocked) uses 4-space. Steering events will appear one visual level shallower than their peers.

  2. Duplicate proxy-events decoder (gateway_logs_timeline.go:529) — proxyEventsEntry + eventName() re-implements the same field-coalescing logic already present in token_usage.go:parseAPIProxySteeringEvents. Future format changes to api-proxy-logs/events.jsonl must be applied in two places.

  3. steeringCount / fwCount decoupling (gateway_logs_timeline_render.go:683) — Steering events are tallied source-independently but reported only under if fwCount > 0. Works today because the source is hardcoded; fragile if that assumption ever changes.

🔎 Code quality review by PR Code Quality Reviewer · ⌖ 13.7 AIC

Comment thread pkg/cli/gateway_logs_timeline_render.go Outdated
case TimelineKindSteering:
icon := streamColor(styles.Warning, timelineEventIcon(TimelineKindSteering))
msg := stringutil.Truncate(evt.Reason, streamMaxAnnotationLen)
fmt.Fprintf(&sb, " %s %s\n", icon, msg)

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.

Indentation inconsistency — steering events will appear visually misaligned in stream output: " %s %s\n" uses 2-space indent while every peer firewall/gateway event case uses 4-space (" %s %s%s\n"), causing steering lines to sit one level shallower than NetworkAllowed, NetworkBlocked, DIFCFiltered, and GuardPolicyBlocked.

💡 Suggested fix

Change the format string from " %s %s\n" to " %s %s\n" (4 spaces) to match all other firewall-sourced event cases in the same switch:

case TimelineKindSteering:
    icon := streamColor(styles.Warning, timelineEventIcon(TimelineKindSteering))
    msg := stringutil.Truncate(evt.Reason, streamMaxAnnotationLen)
    fmt.Fprintf(&sb, "    %s %s\n", icon, msg)   // 4 spaces, not 2

For reference, TimelineKindNetworkAllowed (line ~570) and TimelineKindDIFCFiltered (line ~583) both use " %s %s%s\n". Steering events share TimelineSourceFirewall and should match that visual grouping.

Comment thread pkg/cli/gateway_logs_timeline.go Outdated
// proxyEventsEntry is a JSONL record from api-proxy-logs/events.jsonl.
// The event name appears under one of four field names depending on the proxy version;
// the message field is present on steering events.
type proxyEventsEntry struct {

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.

Duplicate proxy-events parsing logic — format changes will silently diverge: proxyEventsEntry struct + eventName() re-implements the same field-coalescing that token_usage.go already does in parseAPIProxySteeringEvents via map[string]any + coalesceString. Two independent decoders for the same file means any future field name change in api-proxy-logs/events.jsonl requires a coordinated update in both files.

💡 Suggested fix

Move proxyEventsEntry and eventName() to token_usage.go (where the other proxy-parsing constants/helpers already live), then replace the map[string]any+coalesceString path in parseAPIProxySteeringEvents with the typed struct. collectSteeringTimelineEvents can then reuse the same struct:

// token_usage.go (export or share within package)
type proxyEventsEntry struct { ... }  // move here from timeline.go
func (e proxyEventsEntry) eventName() string { ... }

// parseAPIProxySteeringEvents updated to use proxyEventsEntry instead of map[string]any

This is a same-package refactor (no exported surface change) that eliminates the two-file maintenance burden.

fmt.Fprintf(&sb, " Firewall : %d (allowed=%d, blocked=%d)\n",
fwCount, netAllowed, netBlocked)
fwDetail := fmt.Sprintf("allowed=%d, blocked=%d", netAllowed, netBlocked)
if steeringCount > 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.

steeringCount is source-independent but reported only inside if fwCount > 0: the counter is incremented in the flat switch evt.Kind block (no source guard), but emitted only when fwCount > 0. Today this is harmless because steeringEntryToTimelineEvent hardcodes Source: TimelineSourceFirewall. But if any steering event ever arrives from a different source, steeringCount will be non-zero while fwCount may be zero, silently swallowing the steering tally from the summary.

💡 Suggested fix

Count steering under the firewall source bucket explicitly so the coupling is visible and consistent:

case TimelineSourceFirewall:
    fwCount++
    switch evt.Kind {
    case TimelineKindNetworkAllowed:
        netAllowed++
    case TimelineKindNetworkBlocked:
        netBlocked++
    case TimelineKindSteering:
        steeringCount++
    }

Or, at minimum, add a standalone if steeringCount > 0 && fwCount == 0 branch in the summary formatter so the count is never silently dropped.

…plicate proxyEventsEntry

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

Copy link
Copy Markdown
Collaborator

@copilot merge main and recompile

@github-actions

Copy link
Copy Markdown
Contributor

@copilot review all comments and address unresolved review feedback.

Generated by 👨‍🍳 PR Sous Chef · 104.6 AIC · ⌖ 1.48 AIC · ⊞ 17.3K ·

@github-actions

Copy link
Copy Markdown
Contributor

@copilot link the completed ADR in the PR body, then refresh the review request.

Generated by 👨‍🍳 PR Sous Chef · 104.6 AIC · ⌖ 1.48 AIC · ⊞ 17.3K ·

…-of-steering-messages

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

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot merge main and recompile

Done. Merged origin/main (commit 1e237072) into the branch and ran make recompile — all 245 workflows compiled successfully with no new lockfile changes needed. Tests still pass.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot review changes and apply dry / yagni / solid

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

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot review changes and apply dry / yagni / solid

Applied in commit a0a4f0b.

The main DRY violation was the near-identical JSONL scanner loop duplicated between parseAPIProxySteeringEvents (token_usage.go) and collectSteeringTimelineEvents (gateway_logs_timeline.go). Fixed by extracting scanSteeringEntries(r io.Reader) ([]proxyEventsEntry, error) as a shared helper in token_usage.go — both callers now open the file and delegate all scanning/filtering to it. This also aligns the scanner buffer size in parseAPIProxySteeringEvents with the rest of the codebase (maxScannerBufferSize instead of the ad-hoc 64*1024/1024*1024 pair it had before).

@pelikhan pelikhan merged commit b508103 into main Jun 10, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/add-rendering-of-steering-messages branch June 10, 2026 05:33
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.

3 participants