Skip to content

feat: unified event timeline across MCP Gateway, AWF firewall, and agent logs#34782

Merged
pelikhan merged 9 commits into
mainfrom
copilot/extend-gateway-log-parsing
May 25, 2026
Merged

feat: unified event timeline across MCP Gateway, AWF firewall, and agent logs#34782
pelikhan merged 9 commits into
mainfrom
copilot/extend-gateway-log-parsing

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 25, 2026

Merges JSONL event streams from three systems — MCP Gateway (gateway.jsonl/rpc-messages.jsonl), AWF Firewall (audit.jsonl), and agent session (events.jsonl) — into a single chronologically sorted timeline. Renders in two places: Go console output (audit + logs CLI) and JavaScript GITHUB_STEP_SUMMARY.

Event sources and kinds

Source File Kinds
Gateway (GW) mcp-logs/gateway.jsonl or rpc-messages.jsonl tool_call, difc_filtered, guard_blocked
Firewall (FW) sandbox/firewall/audit/audit.jsonl net_allowed, net_blocked
Agent (AG) sandbox/agent/logs/copilot-session-state/<uuid>/events.jsonl agent_turn, agent_tool_start, agent_tool_done

Go changes

  • gateway_logs_timeline.go — adds TimelineSourceAgent, three agent event kinds, parseEventsJSONL helper, collectAgentTimelineEvents (via findEventsJSONLFile); updates BuildUnifiedTimeline to include agent events
  • gateway_logs_timeline_render.go — adds renderAgentTurnRow, renderAgentToolStartRow, renderAgentToolDoneRow; updates timelineSourceLabel (AG), renderTimelineEventRow dispatcher, and renderUnifiedTimeline summary
  • audit.go — adds renderAuditUnifiedTimeline, called from renderAuditReport after renderAuditGatewayMetrics
  • logs_orchestrator.go — calls displayUnifiedTimeline after displayAggregatedGatewayMetrics in the console path
  • gateway_logs_timeline_test.go — unit tests for all three collectors, merge/sort, and rendering primitives

JavaScript changes

  • unified_timeline.cjs — standalone module: collectGatewayEvents, collectFirewallEvents, collectAgentEvents, collectUnifiedTimelineEvents, buildUnifiedTimelineMarkdown (renders a collapsible <details> Markdown table), generateUnifiedTimelineSummary
  • unified_timeline.test.cjs — 66 Vitest tests
  • parse_mcp_gateway_log.cjs — imports unified_timeline.cjs; writeStepSummaryWithTokenUsage appends the timeline to core.summary before every write() call

Timeline output

Console (Go)

Total Events  : 12
  Gateway     : 5  (tool_calls=4, difc_filtered=1, guard_blocked=0)
  Firewall    : 4  (allowed=3, blocked=1)
  Agent       : 3  (turns=1, tool_start=1, tool_done=1)

Time         | Src | Kind              | Detail                | Status
10:00:01.000 | AG  | 💬 agent_turn     | turn 1                |
10:00:02.042 | GW  | 🔧 tool_call      | my-srv/get_file       | success (42ms)
10:00:03.100 | FW  | ✓ net_allowed     | api.github.com:443    | 200

GITHUB_STEP_SUMMARY (JS) — renders as a collapsible <details> block with the same columns.



✨ PR Review Safe Output Test - Run 26422921399

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

💥 [THE END] — Illustrated by Smoke Claude · opus47 4M ·

Copilot AI and others added 4 commits May 25, 2026 21:48
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…aces

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

- Add TimelineSourceAgent + agent event kinds (agent_turn, agent_tool_start,
  agent_tool_done) to gateway_logs_timeline.go
- Add parseEventsJSONL helper and collectAgentTimelineEvents in same file
- Update BuildUnifiedTimeline to include agent events from events.jsonl
- Add rendering primitives for agent events in gateway_logs_timeline_render.go
  (renderAgentTurnRow, renderAgentToolStartRow, renderAgentToolDoneRow)
- Update renderUnifiedTimeline summary to show AG counts
- Wire renderAuditUnifiedTimeline into audit.go (Go audit log generation surface)
- Wire displayUnifiedTimeline into logs_orchestrator.go (Go logs CLI surface)
- Create actions/setup/js/unified_timeline.cjs: JS parser + renderer for
  GITHUB_STEP_SUMMARY (all three sources, sorted merge, Markdown <details> table)
- Create actions/setup/js/unified_timeline.test.cjs: 66 Vitest tests
- Integrate generateUnifiedTimelineSummary into writeStepSummaryWithTokenUsage
  in parse_mcp_gateway_log.cjs so timeline is appended to every step summary
- Create pkg/cli/gateway_logs_timeline_test.go: Go unit tests for all surfaces

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Guard timelineSourceLabel default case against short strings (Go + JS)
- Deduplicate sorted-order check in displayUnifiedTimeline (calls
  sortUnifiedTimelineEvents once instead of inline-checking then calling)
- Fix sortUnifiedTimelineEvents comment to reflect sort.SliceStable usage;
  inline the sort and remove now-unused sortEventsStable helper
- Use truncate() helper for firewall detail in unified_timeline.cjs for
  consistent ellipsis handling with other collectors
- Fix test assertion: replace buggy regex with plain string check for pipe
  escaping in buildUnifiedTimelineMarkdown

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title feat: unified event timeline for MCP Gateway, AWF firewall, and agent logs feat: unified event timeline across MCP Gateway, AWF firewall, and agent logs May 25, 2026
Copilot AI requested a review from pelikhan May 25, 2026 22:13
@pelikhan pelikhan marked this pull request as ready for review May 25, 2026 22:15
Copilot AI review requested due to automatic review settings May 25, 2026 22:15
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

PR Code Quality Reviewer completed the code quality review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

🧪 Test Quality Sentinel completed test quality analysis.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a unified, chronologically sorted event timeline that merges JSONL streams from the MCP Gateway, the AWF firewall, and the agent (Copilot CLI) session. The merged timeline is rendered in two places: the Go CLI (audit and logs console output) and the JavaScript GITHUB_STEP_SUMMARY pipeline, both sharing the same source/kind taxonomy and per-event rendering primitives.

Changes:

  • New Go module (gateway_logs_timeline.go + gateway_logs_timeline_render.go) introducing UnifiedTimelineEvent, three per-source collectors (gateway/firewall/agent), a merge+sort builder, and per-kind table renderers; wired into audit.go and logs_orchestrator.go.
  • New JS module (unified_timeline.cjs) that mirrors the Go collectors/renderers and emits a collapsible <details> Markdown table; appended to step summary via parse_mcp_gateway_log.cjs.
  • Comprehensive Go unit tests and 66 Vitest JS tests for the new collectors and rendering primitives.
Show a summary per file
File Description
pkg/cli/gateway_logs_timeline.go New: unified timeline event types and per-source collectors (gateway/firewall/agent), plus BuildUnifiedTimeline.
pkg/cli/gateway_logs_timeline_render.go New: per-kind row renderers, top-level renderUnifiedTimeline, and displayUnifiedTimeline console entry point.
pkg/cli/gateway_logs_timeline_test.go New: Go unit tests for collectors, merge/sort, and rendering primitives.
pkg/cli/audit.go Calls renderAuditUnifiedTimeline after gateway metrics in audit output.
pkg/cli/logs_orchestrator.go Calls displayUnifiedTimeline in the console-render path.
actions/setup/js/unified_timeline.cjs New: JS port of collectors + Markdown renderer.
actions/setup/js/unified_timeline.test.cjs New: Vitest test suite for the JS module.
actions/setup/js/parse_mcp_gateway_log.cjs Appends the JS-rendered timeline to step summary before write.
.github/workflows/smoke-antigravity.lock.yml Adds --config awf-config.json invocation to awf (appears unrelated to PR purpose).

Copilot's findings

Tip

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

Comments suppressed due to low confidence (2)

actions/setup/js/unified_timeline.cjs:304

  • The JS firewall classification logic diverges from the Go side. Go delegates to the canonical isEntryAllowed(entry) (in firewall_policy.go), whereas this JS uses a regex on the decision field plus a 4xx/5xx HTTP status heuristic. The two will disagree on entries where, for example, a successful CONNECT tunnel has status=0 but a non-matching decision string, or denied entries that don't contain "denied|blocked|reject" but use other Squid status tags (e.g. TCP_MISS_ABORTED, NONE). Since one of the PR's stated goals is rendering the same merged timeline in both Go and JS, the JS classification should mirror the Go logic field-by-field (status >= 400, plus the same set of denied/blocked Squid status prefixes used by isEntryAllowed).
    // Decision field: "TCP_TUNNEL:HIER_DIRECT" or "TCP_DENIED:..." etc.
    const decision = entry.decision ?? entry.squid_request_status ?? "";
    const blocked = /denied|blocked|reject/i.test(decision) ||
      (typeof status === "number" && status >= 400 && status < 600);

    const detail = truncate([host, method].filter(Boolean).join(" "), 48);
    const statusStr = status ? String(status) : (blocked ? "blocked" : "allowed");

    events.push({
      source: SOURCE_FIREWALL,
      kind: blocked ? KIND_NET_BLOCKED : KIND_NET_ALLOWED,
      time,
      detail,
      status: statusStr,
    });

actions/setup/js/unified_timeline.cjs:304

  • Column semantics diverge from the Go renderer. In Go's renderFirewallNetworkAllowedRow/renderFirewallNetworkBlockedRow, the Detail column holds the host alone and the Status column is formatted as METHOD HTTP <code> (e.g. CONNECT HTTP 200). Here, Detail is built as host method concatenated and Status is the raw HTTP status number. Since the JS output is meant to mirror the Go output, please align the column layout: put host in Detail and a formatted "METHOD HTTP nnn" (or "blocked") string in Status.
    const detail = truncate([host, method].filter(Boolean).join(" "), 48);
    const statusStr = status ? String(status) : (blocked ? "blocked" : "allowed");

    events.push({
      source: SOURCE_FIREWALL,
      kind: blocked ? KIND_NET_BLOCKED : KIND_NET_ALLOWED,
      time,
      detail,
      status: statusStr,
    });
  • Files reviewed: 9/9 changed files
  • Comments generated: 3

Comment on lines +281 to +305
for (const entry of parseJsonl(content)) {
// Firewall audit.jsonl uses Unix float64 `ts` field
const time = parseTimestamp(entry.ts);
if (!time) continue;

const host = entry.host ?? entry.domain ?? "";
const method = entry.method ?? "";
const status = entry.status ?? entry.http_status ?? "";

// Decision field: "TCP_TUNNEL:HIER_DIRECT" or "TCP_DENIED:..." etc.
const decision = entry.decision ?? entry.squid_request_status ?? "";
const blocked = /denied|blocked|reject/i.test(decision) ||
(typeof status === "number" && status >= 400 && status < 600);

const detail = truncate([host, method].filter(Boolean).join(" "), 48);
const statusStr = status ? String(status) : (blocked ? "blocked" : "allowed");

events.push({
source: SOURCE_FIREWALL,
kind: blocked ? KIND_NET_BLOCKED : KIND_NET_ALLOWED,
time,
detail,
status: statusStr,
});
}
Comment on lines +1 to +14
// This file contains rendering primitives and the top-level render function for the
// unified MCP Gateway + AWF firewall timeline produced by BuildUnifiedTimeline.
//
// A dedicated rendering primitive exists for every TimelineEventKind so that each event
// type is displayed with appropriate context and formatting:
//
// TimelineKindToolCall – renderGatewayToolCallRow
// TimelineKindDIFCFiltered – renderGatewayDIFCFilteredRow
// TimelineKindGuardPolicyBlocked – renderGatewayGuardPolicyBlockedRow
// TimelineKindNetworkAllowed – renderFirewallNetworkAllowedRow
// TimelineKindNetworkBlocked – renderFirewallNetworkBlockedRow
//
// renderTimelineEventRow dispatches to the appropriate primitive and returns a
// []string suitable for inclusion in a console.TableConfig.Rows slice.
Comment on lines +921 to +928
printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","*.googleapis.com","*.grafana.net","*.sentry.io","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","generativelanguage.googleapis.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"targets":{"antigravity":{"host":"generativelanguage.googleapis.com"}},"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
fi
# shellcheck disable=SC1003
sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env ANTIGRAVITY_API_KEY --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \
sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env ANTIGRAVITY_API_KEY --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \
@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (1,543 new lines across business directories, ≫ 100-line threshold) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/34782-unified-event-timeline-across-mcp-gateway-firewall-agent.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 (Go + JS sources, both rendering surfaces, schema normalization, integration points).
  2. Complete the missing sections — add context the AI couldn't infer (e.g., who decided this, prior debugging incidents that motivated it), refine the decision rationale, and verify the alternatives reflect what you actually considered.
  3. Tighten the normative spec (Part 2) — confirm every MUST/SHOULD matches the implementation; tighten or relax keywords as appropriate.
  4. Commit the finalized ADR to docs/adr/ on your branch.
  5. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-34782: Unified Event Timeline Across MCP Gateway, AWF Firewall, and Agent Logs

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

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

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., 34782-*.md for PR #34782).

References: §26421888908

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · opus47 5.5M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 2.2M

// section regardless of which gateway log format was detected above.
const timelineMd = generateUnifiedTimelineSummary();
if (timelineMd) {
coreObj.info(`Appending unified event timeline to step summary`);
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.

Timeline summary call is unguarded — a throw here will silently drop all previously buffered step-summary content.

💡 Suggested fix

generateUnifiedTimelineSummary() calls fs.readFileSync and JSON.parse under the hood. If either throws (e.g. a corrupt JSONL file, a permission error, or an unexpected log shape), the exception propagates and coreObj.summary.write() is never reached. Any token-usage stats or other sections already accumulated by the caller are silently discarded.

Wrap in try/catch:

try {
  const timelineMd = generateUnifiedTimelineSummary();
  if (timelineMd) {
    coreObj.info('Appending unified event timeline to step summary');
    coreObj.summary.addRaw(timelineMd);
  }
} catch (err) {
  coreObj.warning(`Failed to generate unified timeline: ${err?.message ?? err}`);
}
coreObj.summary.write();

This matches how other optional enrichment sections are handled elsewhere in the file.

} catch {
// ignore read errors
}
return null;
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.

undefined success field is coerced to "error" status, misclassifying every tool completion from older agent versions.

💡 Suggested fix
case "tool.execution_complete": {
  const server = entry.data?.mcpServerName ?? "";
  const tool = entry.data?.toolName ?? "";
  const success = entry.data?.success;
  // ...
  status: success ? "success" : "error",   // ← undefined → "error"
}

When the agent session events.jsonl format does not include a success field (field absent or emitting agent version predates it), entry.data?.success evaluates to undefined, which is falsy. Every tool completion is then classified as "error" — the opposite of the likely reality and a visually alarming misrepresentation in the step summary.

The Go counterpart uses a typed bool so the zero-value default is consistent; JS needs an explicit check:

const success = entry.data?.success;
const statusStr = success === true ? "success" : success === false ? "error" : "unknown";

Or at minimum guard against the undefined case:

status: success == null ? "" : success ? "success" : "error",

const blocked = /denied|blocked|reject/i.test(decision) ||
(typeof status === "number" && status >= 400 && status < 600);

const detail = truncate([host, method].filter(Boolean).join(" "), 48);
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.

findEventsJsonlFile returns the first directory entry it finds — non-deterministic when multiple session directories exist.

💡 Suggested fix

readdirSync returns entries in filesystem order, which is platform-dependent (ext4 on Linux returns entries in hash-tree order, not creation order). When an agent retries or a prior run's state directory is left behind, the function picks an arbitrary session's events.jsonl — potentially the wrong run's data.

Sort by most-recently modified before returning:

const entries = fs.readdirSync(sessionStateDir, { withFileTypes: true });
const candidates = entries
  .filter(e => e.isDirectory())
  .map(e => {
    const p = path.join(sessionStateDir, e.name, "events.jsonl");
    try { return { p, mtime: fs.statSync(p).mtimeMs }; } catch { return null; }
  })
  .filter(Boolean);
candidates.sort((a, b) => b.mtime - a.mtime);
return candidates[0]?.p ?? null;

The Go counterpart (findEventsJSONLFile) has the same issue and should be fixed in parallel.

if (entry.type === "DIFC_FILTERED") {
const server = entry.server_id ?? entry.server_name ?? "";
const tool = entry.tool_name ?? "";
events.push({
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.

HTTP 4xx status codes are classified as firewall-blocked, conflating application errors with security denials.

💡 Suggested fix
const blocked = /denied|blocked|reject/i.test(decision) ||
  (typeof status === "number" && status >= 400 && status < 600);

A 404 Not Found, 401 Unauthorized, or 429 Too Many Requests from the upstream server is not a firewall block — the firewall permitted the connection and the server responded with an error. Treating these as net_blocked events inflates the blocked count and will confuse operators investigating security incidents.

Also, the regex matches any substring: "TCP_TUNNEL_UNBLOCKED" would match blocked, and a decision string like "not_rejected" would match reject. Use word-boundary anchors or explicit string comparison:

const blocked = /\bdenied\b|\bblocked\b|\brejected\b|TCP_DENIED/i.test(decision);
// HTTP status is not a reliable block signal — omit it

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 88/100 — Excellent

Analyzed 73 tests (23 Go + 50 JavaScript): 69 design tests (behavioral contracts), 4 implementation tests (5%), 0 guideline violations.

📊 Metrics & Test Classification (73 tests analyzed)
Metric Value
New/modified tests analyzed 73
✅ Design tests (behavioral contracts) 69 (95%)
⚠️ Implementation tests (low value) 4 (5%)
Tests with error/edge cases 55 (75%)
Duplicate test clusters 0
Test inflation detected No (Go 0.54×, JS 1.14×)
🚨 Coding-guideline violations 0

Test Classification Details

Go tests (23)
Test File Classification Notes
TestUnifiedTimelineEvent_Fields gateway_logs_timeline_test.go:35 ⚠️ Implementation Tests struct field assignment; no logic exercised
TestParseEventsJSONL_BasicTypes :56 ✅ Design Parses multi-type JSONL; verifies decoded fields
TestParseEventsJSONL_MalformedLinesSkipped :110 ✅ Design Edge case: malformed lines are skipped gracefully
TestAgentEntryToTimelineEvent_UserMessage :131 ✅ Design Verifies kind, turnIndex, source mapping
TestAgentEntryToTimelineEvent_ToolStart :151 ✅ Design Verifies tool-start transformation fields
TestAgentEntryToTimelineEvent_ToolDoneSuccess :176 ✅ Design Verifies success status derivation
TestAgentEntryToTimelineEvent_ToolDoneError :201 ✅ Design Error case: success=false → status "error"
TestAgentEntryToTimelineEvent_SessionStartSkipped :220 ✅ Design Edge: session.start must be filtered out
TestAgentEntryToTimelineEvent_BadTimestamp :231 ✅ Design Error case: unparseable timestamp → ok=false
TestCollectAgentTimelineEvents_ReturnsNilWhenMissing :244 ✅ Design Edge: missing file → nil, no error
TestCollectAgentTimelineEvents_ReadsCanonicalPath :255 ✅ Design Integration of path discovery + parsing
TestBuildUnifiedTimeline_EmptyDir :304 ✅ Design Edge: empty directory → zero events
TestBuildUnifiedTimeline_SortsMixedSources :315 ✅ Design Verifies cross-source chronological ordering
TestRenderUnifiedTimeline_Empty :362 ✅ Design Edge: nil input → empty string
TestRenderUnifiedTimeline_AllSources :369 ✅ Design Behavioral: verifies GW/FW/AG labels and kind labels in output
TestRenderUnifiedTimeline_AgentCountsInSummary :415 ✅ Design Verifies turns=1 counter in summary output
TestRenderAgentTurnRow :434 ✅ Design Verifies row column count and turn N label
TestRenderAgentToolStartRow_WithServer :453 ✅ Design Verifies server/tool formatting
TestRenderAgentToolStartRow_WithoutServer :468 ✅ Design Edge: tool name without server prefix
TestRenderAgentToolDoneRow_StatusFromField :481 ✅ Design Verifies explicit status field used when set
TestRenderAgentToolDoneRow_StatusFromSuccessFlag :495 ✅ Design Edge: derives status from Success=true when field empty
TestTimelineEventIcon_AllKinds :511 ✅ Design Verifies all 8 kinds return non-default icons
TestTimelineSourceLabel_Agent :530 ⚠️ Implementation Tests only one of three source labels; trivially incomplete
JavaScript tests (50 — sampled to 50)

⚠️ Sampling applied — analyzed the first 50 of 50 test functions (all covered).

Describe / it Classification Notes
parseJsonl × 5 ✅ Design Covers valid, blank lines, non-{ lines, malformed JSON, empty string
parseTimestamp × 5 ✅ Design RFC3339, unix float, null, undefined, invalid string — full contract
formatTime × 2 ✅ Design UTC format and zero-padding — behavioral contract
truncate × 4 ✅ Design Shorter, equal, longer, empty — complete boundary set
sourceLabel it.each ✅ Design All three sources verified
eventIcon × 2 ✅ Design Default and non-default icons for all kinds
kindLabel it.each ✅ Design All kind labels verified
collectGatewayEvents × 5 ✅ Design Empty, tool_call, DIFC_FILTERED, GUARD_POLICY_BLOCKED, fallback file, invalid timestamps
collectFirewallEvents × 4 ✅ Design Missing file, allowed/blocked decisions, 4xx/5xx → net_blocked, missing ts
findEventsJsonlFile × 2 ✅ Design Non-existent dir and canonical UUID path discovery
collectAgentEvents × 6 ✅ Design Empty, user.message, tool.execution_start/complete, session.start skip, logDir path
collectUnifiedTimelineEvents × 2 ✅ Design Empty files, merged+sorted multi-source output
buildUnifiedTimelineMarkdown × 6 ✅ Design Empty, null/undefined, <details> wrapping, source labels, table header, pipe escaping
generateUnifiedTimelineSummary × 2 ✅ Design Missing files → empty string, at least one source → non-empty
collectGatewayEvents rpc-messages fallback ⚠️ Implementation Overlaps with the "falls back to rpc-messages.jsonl" case above
collectGatewayEvents timestamp skip ⚠️ Implementation Slight overlap with collectAgentEvents invalid-timestamp test

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 23 tests — unit (//go:build !integration) ✅ build tag present
  • 🟨 JavaScript (*.test.cjs): 50 tests (vitest) — no vi.mock() / vi.spyOn() / vi.fn() used; real fs and temp dirs throughout
⚠️ Flagged Tests — Minor Issues (4)

⚠️ TestUnifiedTimelineEvent_Fields (gateway_logs_timeline_test.go:35)

Classification: Implementation test
Issue: Constructs a struct literal and asserts the fields equal what was just assigned. No parsing, transformation, or logic path is exercised.
What would break if deleted? Only a refactoring that renames constants — not a behavioral regression.
Suggested improvement: Fold into TestParseEventsJSONL_BasicTypes as a field-level assertion, or drop if the struct constant values are already covered elsewhere.


⚠️ TestTimelineSourceLabel_Agent (gateway_logs_timeline_test.go:530)

Classification: Implementation test (incomplete coverage)
Issue: Only checks the "AG" label for TimelineSourceAgent; TimelineSourceGateway and TimelineSourceFirewall are left unverified.
What would break if deleted? A typo in a single constant — the other two source labels would remain uncovered.
Suggested improvement: Expand to a table-driven test covering all three TimelineSource constants, similar to the JS sourceLabel it.each test.


⚠️ collectGatewayEvents rpc-messages fallback + timestamp-skip (JS)

Classification: Minor duplication (2 test cases)
Issue: The fallback-file and invalid-timestamp scenarios are each tested in both collectGatewayEvents and collectAgentEvents, with nearly identical structure.
Impact: Low — duplicate coverage is harmless here, and each covers a slightly different code path.
Suggested improvement: Consider a shared helper for "skips entries with bad timestamps" to reduce duplication, but this is optional.

Verdict

Check passed. Only 5% of new tests are implementation tests (threshold: 30%). No coding-guideline violations detected.

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 3.5M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 88/100. Test quality is excellent — only 5% of new tests are implementation tests (threshold: 30%). No coding-guideline violations detected.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 4.7M

// Append the unified event timeline (gateway + firewall audit + agent events)
// to the step summary immediately before flushing, so it appears as the last
// section regardless of which gateway log format was detected above.
const timelineMd = generateUnifiedTimelineSummary();
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] generateUnifiedTimelineSummary() is called without a try/catch guard. If it throws for any reason (unexpected JSONL shape, fs permission error, etc.), the coreObj.summary.write() call on the next line is never reached — silently dropping the entire step summary for every consumer of this function.

💡 Suggested fix

Wrap the new block in a try/catch so a timeline failure degrades gracefully:

try {
  const timelineMd = generateUnifiedTimelineSummary();
  if (timelineMd) {
    coreObj.info('Appending unified event timeline to step summary');
    coreObj.summary.addRaw(timelineMd);
  }
} catch (err) {
  coreObj.warning(`Failed to generate unified event timeline: ${err?.message ?? err}`);
}

A test in unified_timeline.test.cjs that mocks fs.readFileSync to throw and asserts generateUnifiedTimelineSummary returns "" (rather than propagating) would catch this class of regression before it reaches production.

return UnifiedTimelineEvent{}, false
}
evt.Kind = TimelineKindToolCall
evt.Status = "initiated"
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.

[/grill-with-docs] Status: "initiated" is a third status value not documented anywhere in the codebase — every other tool call event uses "success" or "error". When rpc-messages.jsonl is the source (i.e., gateway.jsonl is absent), all tool-call rows will show "initiated" in the Status column instead of an outcome, making the timeline look incomplete or stalled.

💡 Options

Option A — Document the semantic gap: Add a comment explaining that rpc-messages entries capture the request only (no response), so "initiated" is intentionally different:

// "initiated" is used instead of "success"/"error" because rpc-messages.jsonl
// records only the outbound request; the response/outcome is not available.
evt.Status = "initiated"

Option B — Use a different label ("→" or "sent") that visually distinguishes in-flight entries from completed ones, and add a test that verifies this label is preserved through renderGatewayToolCallRow.

Either way, TestRpcEntryToTimelineEvent_RequestStatus would be a useful test addition.

gatewayLogsLog.Printf("collectAgentTimelineEvents error: %v", agErr)
}

events := make([]UnifiedTimelineEvent, 0, len(gatewayEvents)+len(firewallEvents)+len(agentEvents))
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] BuildUnifiedTimeline returns (events, nil) even when one or more collectors fail — errors are only logged at debug level. A caller that receives an empty slice cannot distinguish "no events found" from "all three collectors errored", making silent regressions invisible.

💡 Suggestion

Consider returning a sentinel or a multi-error to surface partial failures. At minimum, the function could return the first non-nil error so callers can decide:

var firstErr error
gatewayEvents, gwErr := collectGatewayTimelineEvents(logDir, verbose)
if gwErr != nil {
    gatewayLogsLog.Printf("collectGatewayTimelineEvents error: %v", gwErr)
    firstErr = gwErr
}
// ... same for firewall/agent ...

return events, firstErr

This is especially important for renderAuditUnifiedTimeline in audit.go, which currently silences errors on both the BuildUnifiedTimeline call and inside the function itself.

}
}

// ─── BuildUnifiedTimeline ────────────────────────────────────────────────────
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] collectGatewayTimelineEvents and collectFirewallTimelineEvents have no direct unit tests — they are only exercised indirectly via BuildUnifiedTimeline. Missing coverage for the error paths in each collector (e.g., os.Open failure, malformed mid-stream JSONL that causes a scanner error) means a file-permission regression would surface only in production.

💡 Suggested additions
func TestCollectGatewayTimelineEvents_UnreadableFile(t *testing.T) {
    dir := t.TempDir()
    p := filepath.Join(dir, "gateway.jsonl")
    require.NoError(t, os.WriteFile(p, []byte(`{"event":"tool_call","timestamp":"2024-01-15T10:00:00Z"}`+"\n"), 0000))
    t.Cleanup(func() { _ = os.Chmod(p, 0600) })

    _, err := collectGatewayTimelineEvents(dir, false)
    assert.Error(t, err)
}

func TestCollectFirewallTimelineEvents_EmptyDir(t *testing.T) {
    events, err := collectFirewallTimelineEvents(t.TempDir(), false)
    assert.NoError(t, err)
    assert.Empty(t, events)
}

These two collectors handle the bulk of real-world data and deserve the same direct test coverage as collectAgentTimelineEvents.

case "tool.execution_complete": {
const server = entry.data?.mcpServerName ?? "";
const tool = entry.data?.toolName ?? "";
const success = entry.data?.success;
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] entry.data?.success is undefined when the field is absent (rather than explicitly false). Since undefined is falsy, success ? "success" : "error" evaluates to "error" for any tool.execution_complete event that simply omits the success field — producing a false negative.

💡 Suggested fix

Check for false explicitly to distinguish "failed" from "field missing":

// Treat absent success field as success (default), explicit false as error.
const status = entry.data?.success === false ? "error" : "success";

Add a test for the missing-field case:

it("treats missing success field as success", () => {
  // entry.data has no "success" key at all
  const events = collectAgentEvents({ eventsJsonlPath: pathToFileWith({ type: "tool.execution_complete", data: { toolName: "x" } }) });
  expect(events[0].status).toBe("success");
});

// Path constants
// ---------------------------------------------------------------------------

const TMP_GH_AW = "/tmp/gh-aw";
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.

[/grill-with-docs] Path constants are duplicated between this file and the Go implementation (findEventsJSONLFile in copilot_events_jsonl.go, findGatewayJSONLPath in gateway_logs_timeline.go). A path change in Go (e.g., renaming copilot-session-state) would silently produce empty timelines in the JS step summary.

💡 Suggestion

Add a comment that cross-references the canonical Go sources, so future changes prompt the author to update both sides:

// These paths MUST stay in sync with their Go counterparts:
//   pkg/cli/copilot_events_jsonl.go  → findEventsJSONLFile
//   pkg/cli/gateway_logs_timeline.go → findGatewayJSONLPath
//   pkg/cli/firewall_policy.go       → detectFirewallAuditArtifacts
const TMP_GH_AW = "/tmp/gh-aw";

Longer-term, a constants.cjs that is referenced by both this file and gateway-log parsers would be the cleanest approach.

// displayUnifiedTimeline collects all JSONL events from every processed run, merges them
// into a single chronologically ordered stream, and writes the rendered timeline to
// stderr. It is a no-op when no events can be collected from any run.
func displayUnifiedTimeline(processedRuns []ProcessedRun, verbose bool) {
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.

[/zoom-out] When displayUnifiedTimeline merges events across multiple processed runs, the output interleaves events from different runs chronologically without any run boundary label. For a gh aw logs invocation covering 2+ runs, it is impossible to tell which run produced a given event — undermining the diagnostic value of the timeline.

💡 Suggestion

Either render a per-run timeline (one table per run) or add a "Run" column to distinguish events. A per-run approach integrates naturally with the existing ProcessedRun loop:

for _, pr := range processedRuns {
    events, err := BuildUnifiedTimeline(pr.Run.LogsPath, verbose)
    // ...
    fmt.Fprintf(os.Stderr, "\nRun #%d\n", pr.Run.DatabaseID)
    if output := renderUnifiedTimeline(events); output != "" {
        fmt.Fprint(os.Stderr, output)
    }
}

The current merge-and-re-sort approach is reasonable for single-run output (which is the common case for gh aw audit), but worth reconsidering for the logs path where multiple runs are typical.

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.

Smoke bot reply. Me saw latest review comment.

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

📰 BREAKING: Report filed by Smoke Copilot · gpt55 3.5M

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot Review all comments and pull-request review comments, address what’s needed, and resolve conversations based on your changes.

Copilot AI and others added 3 commits May 25, 2026 22:38
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…k file revert

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot Review all comments and pull-request review comments, address what’s needed, and resolve conversations based on your changes.

Addressed all three review threads:

  1. JS firewall filters (unified_timeline.cjs:281-305): Ported both Go filters to collectFirewallEvents — skips entries where url === "error:transaction-end-before-headers" and entries with empty or "-" host. Added 2 new tests covering both filter cases (68 total). Also simplified the url field lookup to use only lowercase entry.url, matching the Go struct's json:"url" tag.

  2. Go render file header (gateway_logs_timeline_render.go:1-14): Updated the file-level doc comment to describe the unified MCP Gateway + AWF Firewall + Agent timeline, and added all three new agent kind → primitive mappings (TimelineKindAgentTurn, TimelineKindAgentToolStart, TimelineKindAgentToolDone).

  3. Lock file change (.github/workflows/smoke-antigravity.lock.yml): Reverted the unrelated --config flag and cp additions back to the pre-PR state (efa5376).

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

🚀 Smoke Gemini MISSION COMPLETE! Gemini has spoken. ✨

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

🚀 Smoke Pi MISSION COMPLETE! Pi delivered. 🥧

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Copy Markdown
Contributor

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions github-actions Bot removed the smoke label May 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

🌑 The shadows whisper... Smoke Codex failed. The oracle requires further meditation...

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

⚠️ Smoke Antigravity failed. Antigravity encountered unexpected challenges...

@github-actions
Copy link
Copy Markdown
Contributor

Agent Container Tool Check

Tool Status Version
bash 5.2.21
sh available
git 2.54.0
jq 1.7
yq 4.53.2
curl 8.5.0
gh 2.92.0
node 22.22.3
python3 3.14.5
go 1.24.13
java openjdk 21.0.11
dotnet 10.0.300

Result: 12/12 tools available ✅

Overall Status: PASS

🔧 Tool validation by Agent Container Smoke Test · sonnet46 504.4K ·

@github-actions
Copy link
Copy Markdown
Contributor

  • GitHub MCP Testing: ✅
  • Web Fetch Testing: ✅
  • File Writing Testing: ✅
  • Bash Tool Testing: ✅
  • Build gh-aw: ❌
    Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

Smoke Gemini — Powered by Gemini ·

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: Claude — Run §26422921399

Core tests #1–13: ✅ all passed
PR review tests #14–19: ✅ all passed · #20: ⚠️ skipped (no safe test PR)

Overall: PASS

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

💥 [THE END] — Illustrated by Smoke Claude · opus47 4M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

💥 Automated smoke test review - all systems nominal!

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

💥 [THE END] — Illustrated by Smoke Claude · opus47 4M

@@ -0,0 +1,540 @@
// This file implements unified timeline merging for MCP Gateway, AWF firewall, and agent
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.

Smoke test: consider adding a brief package-level comment describing the timeline parsing approach.

@@ -0,0 +1,553 @@
// @ts-check
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.

Smoke test: a top-level JSDoc summarizing inputs/outputs would help readers navigate this 550+ line file.

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Copilot 26422921402: FAIL
PR: feat: unified event timeline across MCP Gateway, AWF firewall, and agent logs
Author: app/copilot-swe-agent; assignees: pelikhan, Copilot
✅ merged PRs, Serena, Playwright, file/bash, discussion, build/artifact, workflow, PR review, sub-agent, check run
❌ mcpscripts-gh, web-fetch, comment-memory

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

📰 BREAKING: Report filed by Smoke Copilot · gpt55 3.5M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Smoke review complete. Me leave non-blocking notes only.

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

📰 BREAKING: Report filed by Smoke Copilot · gpt55 3.5M

const GATEWAY_JSONL_PATH = `${TMP_GH_AW}/mcp-logs/gateway.jsonl`;
const RPC_MESSAGES_PATH = `${TMP_GH_AW}/mcp-logs/rpc-messages.jsonl`;
const FIREWALL_AUDIT_PATH = `${TMP_GH_AW}/sandbox/firewall/audit/audit.jsonl`;
/** Base directory to search recursively for events.jsonl */
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.

Smoke review note: these path constants look important to keep aligned with the Go log collectors.

"time"
)

// TimelineEventSource identifies which system produced a timeline event.
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.

Smoke review note: unified source constants make the merged timeline easier to reason about.

@github-actions
Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

Copy link
Copy Markdown
Collaborator

@pelikhan pelikhan left a comment

Choose a reason for hiding this comment

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

@copilot does not seem to be configured properly

displayAggregatedGatewayMetrics(processedRuns, opts.outputDir, opts.verbose)

// Display unified event timeline (gateway + firewall + agent events)
displayUnifiedTimeline(processedRuns, opts.verbose)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@copilot guard by --timeline flag in logs command

*
* Path constants mirror the values in constants.cjs and the Go package.
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@copilot add logging using core...

@pelikhan pelikhan merged commit 5b632a2 into main May 25, 2026
30 of 34 checks passed
@pelikhan pelikhan deleted the copilot/extend-gateway-log-parsing branch May 25, 2026 23:18
Copilot stopped work on behalf of pelikhan due to an error May 25, 2026 23:19
Copilot AI requested a review from pelikhan May 25, 2026 23:19
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.

3 participants