-
Notifications
You must be signed in to change notification settings - Fork 1
Add weekly parse attribution and keepalive NDJSON repair #1878
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,16 +6,61 @@ | |||||||||||||||||||||||||||||||||
| import datetime as _dt | ||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||
| from collections import Counter | ||||||||||||||||||||||||||||||||||
| from collections.abc import Iterable | ||||||||||||||||||||||||||||||||||
| from dataclasses import dataclass | ||||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| _DEFAULT_METRICS_DIR = "agent-metrics" | ||||||||||||||||||||||||||||||||||
| _DEFAULT_OUTPUT = "agent-metrics-summary.md" | ||||||||||||||||||||||||||||||||||
| _DEFAULT_JSON_OUTPUT = "agent-metrics-summary.json" | ||||||||||||||||||||||||||||||||||
| _DEFAULT_UNSUPPORTED_VERIFIER_MODELS = {"gpt-5.2-codex"} | ||||||||||||||||||||||||||||||||||
| _DEFAULT_VERIFIER_MODEL_METADATA_REQUIRED_AFTER = "2026-04-26T04:25:00Z" | ||||||||||||||||||||||||||||||||||
| _EXACT_ARTIFACT_FAMILIES = { | ||||||||||||||||||||||||||||||||||
| "keepalive-metrics", | ||||||||||||||||||||||||||||||||||
| "agents-autofix-metrics", | ||||||||||||||||||||||||||||||||||
| "agents-verifier-metrics", | ||||||||||||||||||||||||||||||||||
| "agents-verifier-disposition-metrics", | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| _PREFIXED_ARTIFACT_FAMILIES = ( | ||||||||||||||||||||||||||||||||||
| "autopilot-metrics-", | ||||||||||||||||||||||||||||||||||
| "issue-optimizer-metrics-", | ||||||||||||||||||||||||||||||||||
| "issue-intake-format-metrics-", | ||||||||||||||||||||||||||||||||||
| "verifier-terminal-disposition-", | ||||||||||||||||||||||||||||||||||
| "review-thread-terminal-disposition-", | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| _PATTERNED_ARTIFACT_FAMILIES = ( | ||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||
| "bot-comment-auth-coverage-wrapper", | ||||||||||||||||||||||||||||||||||
| re.compile(r"^bot-comment-auth-coverage-wrapper(?:-[A-Za-z0-9][A-Za-z0-9._-]*)?$"), | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||
| "bot-comment-auth-coverage-reusable", | ||||||||||||||||||||||||||||||||||
| re.compile(r"^bot-comment-auth-coverage-reusable(?:-[A-Za-z0-9][A-Za-z0-9._-]*)?$"), | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| _MAX_PARSE_ERROR_ROWS = 25 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @dataclass(frozen=True) | ||||||||||||||||||||||||||||||||||
| class ParseErrorDetail: | ||||||||||||||||||||||||||||||||||
| path: str | ||||||||||||||||||||||||||||||||||
| artifact: str | ||||||||||||||||||||||||||||||||||
| artifact_family: str | ||||||||||||||||||||||||||||||||||
| line: int | None | ||||||||||||||||||||||||||||||||||
| reason: str | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def as_dict(self) -> dict[str, Any]: | ||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||
| "path": self.path, | ||||||||||||||||||||||||||||||||||
| "artifact": self.artifact, | ||||||||||||||||||||||||||||||||||
| "artifact_family": self.artifact_family, | ||||||||||||||||||||||||||||||||||
| "line": self.line, | ||||||||||||||||||||||||||||||||||
| "reason": self.reason, | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _parse_timestamp(value: Any) -> _dt.datetime | None: | ||||||||||||||||||||||||||||||||||
|
|
@@ -52,29 +97,82 @@ def _gather_metrics_files(metrics_paths: list[str], metrics_dir: str) -> list[Pa | |||||||||||||||||||||||||||||||||
| return sorted(path for path in root.rglob("*.ndjson") if path.is_file()) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _read_ndjson(files: Iterable[Path]) -> tuple[list[dict[str, Any]], int]: | ||||||||||||||||||||||||||||||||||
| def _artifact_family(artifact: str) -> str: | ||||||||||||||||||||||||||||||||||
| if artifact in _EXACT_ARTIFACT_FAMILIES: | ||||||||||||||||||||||||||||||||||
| return artifact | ||||||||||||||||||||||||||||||||||
| for family, pattern in _PATTERNED_ARTIFACT_FAMILIES: | ||||||||||||||||||||||||||||||||||
| if pattern.match(artifact): | ||||||||||||||||||||||||||||||||||
| return family | ||||||||||||||||||||||||||||||||||
| for prefix in _PREFIXED_ARTIFACT_FAMILIES: | ||||||||||||||||||||||||||||||||||
| if artifact.startswith(prefix): | ||||||||||||||||||||||||||||||||||
| return prefix.rstrip("-") | ||||||||||||||||||||||||||||||||||
| return "unknown" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _infer_artifact_name(path: Path) -> str: | ||||||||||||||||||||||||||||||||||
| parts = path.parts | ||||||||||||||||||||||||||||||||||
| for index, part in enumerate(parts): | ||||||||||||||||||||||||||||||||||
| if part == "agent-metrics" and index > 0: | ||||||||||||||||||||||||||||||||||
| return parts[index - 1] | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| return parts[index - 1] | |
| candidate = parts[index - 1] | |
| if _artifact_family(candidate) != "unknown": | |
| return candidate |
Copilot
AI
Apr 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_parse_error_contract() includes every parse error detail in the JSON contract. A single corrupted multi-line file can generate a very large details array, which can bloat agent-weekly-metrics.json and uploaded artifacts. Consider capping the details list (similar to _MAX_PARSE_ERROR_ROWS) and adding an omitted_count/details_truncated field so consumers can detect truncation.
| return { | |
| "count": len(parse_error_details), | |
| "by_artifact_family": dict(sorted(family_counts.items())), | |
| "by_artifact": dict(sorted(artifact_counts.items())), | |
| "by_reason": dict(sorted(reason_counts.items())), | |
| "details": [detail.as_dict() for detail in parse_error_details], | |
| details = [detail.as_dict() for detail in parse_error_details[:_MAX_PARSE_ERROR_ROWS]] | |
| omitted_count = max(0, len(parse_error_details) - len(details)) | |
| return { | |
| "count": len(parse_error_details), | |
| "by_artifact_family": dict(sorted(family_counts.items())), | |
| "by_artifact": dict(sorted(artifact_counts.items())), | |
| "by_reason": dict(sorted(reason_counts.items())), | |
| "details": details, | |
| "details_truncated": omitted_count > 0, | |
| "omitted_count": omitted_count, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module-level docstring says this script aggregates NDJSON into a markdown summary, but it now also writes a JSON summary contract (via
OUTPUT_JSON_PATH). Update the docstring so it accurately reflects the outputs and environment variables.