Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/scripts/__tests__/terminal-disposition-coverage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,35 @@ test('normalizes artifact selection reports without selected family counts', ()
);
});

test('normalizes legacy artifact selection latest candidate maps', () => {
const summary = normalizeArtifactSelectionSummary({
status: 'pass',
candidate_family_counts: {
'review-thread-terminal-disposition': 2,
},
selected_family_counts: {},
latest_candidate_by_family: {
'review-thread-terminal-disposition': {
id: 44,
name: 'review-thread-terminal-disposition-latest',
created_at: '2026-04-25T12:00:00Z',
},
},
selected_artifacts: [],
});

const reviewThreadStatus = summary.terminal_priority_family_statuses.find(
(status) => status.family === 'review-thread-terminal-disposition'
);
assert.equal(reviewThreadStatus.status, 'available');
assert.equal(reviewThreadStatus.selected_artifact, null);
assert.deepEqual(reviewThreadStatus.latest_candidate, {
id: 44,
name: 'review-thread-terminal-disposition-latest',
created_at: '2026-04-25T12:00:00Z',
});
});

test('collects only terminal disposition ndjson files from metrics artifacts', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'terminal-coverage-'));
const terminalDir = path.join(dir, 'review-thread-terminal-disposition-123', 'agent-metrics');
Expand Down
41 changes: 41 additions & 0 deletions .github/scripts/__tests__/weekly-metrics-artifacts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
collectRepoArtifacts,
formatArtifactTsv,
formatSelectionMarkdown,
latestCandidateByFamily,
missingPriorityFamilies,
normalizeSelectionOptions,
priorityFamilyStatuses,
Expand Down Expand Up @@ -102,6 +103,23 @@ test('selects only recent matching artifacts with a machine-readable report', ()
'verifier-terminal-disposition',
'bot-comment-auth-coverage-reusable',
]);
assert.deepEqual(
report.latest_candidate_by_family,
{
'bot-comment-auth-coverage-wrapper': {
id: 8,
name: 'bot-comment-auth-coverage-wrapper-77-2',
created_at: '2026-04-25T08:30:00Z',
updated_at: '2026-04-25T08:30:00Z',
},
'review-thread-terminal-disposition': {
id: 5,
name: 'review-thread-terminal-disposition-77',
created_at: '2026-04-25T11:00:00Z',
updated_at: '2026-04-25T11:00:00Z',
},
}
);
assert.deepEqual(
report.priority_family_statuses.map((family) => ({
family: family.family,
Expand Down Expand Up @@ -250,6 +268,29 @@ test('reports priority telemetry families that are absent from the scan', () =>
]);
});

test('maps latest candidate artifacts by priority family', () => {
const candidates = [
artifact(1, 'review-thread-terminal-disposition-older', '2026-04-25T10:00:00Z'),
artifact(2, 'review-thread-terminal-disposition-newer', '2026-04-25T11:00:00Z'),
artifact(3, 'keepalive-metrics', '2026-04-25T12:00:00Z'),
].map((raw) => ({
id: raw.id,
name: raw.name,
family: artifactFamily(raw.name),
created_at: raw.created_at,
updated_at: raw.updated_at,
}));

assert.deepEqual(latestCandidateByFamily(candidates), {
'review-thread-terminal-disposition': {
id: 2,
name: 'review-thread-terminal-disposition-newer',
created_at: '2026-04-25T11:00:00Z',
updated_at: '2026-04-25T11:00:00Z',
},
});
});

test('builds priority family statuses for available but unselected artifacts', () => {
const candidates = [
artifact(1, 'bot-comment-auth-coverage-wrapper-1', '2026-04-25T11:00:00Z'),
Expand Down
19 changes: 18 additions & 1 deletion .github/scripts/terminal_disposition_coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,21 @@ function normalizeSelectionArtifact(artifact = {}) {
return normalized;
}

function latestCandidateFromSelectionReport(report = {}, family = '') {
const candidateMaps = [
report.latest_candidate_by_family,
report.latest_candidates_by_family,
];
for (const candidatesByFamily of candidateMaps) {
if (!candidatesByFamily || typeof candidatesByFamily !== 'object' || Array.isArray(candidatesByFamily)) {
continue;
}
const candidate = normalizeSelectionArtifact(candidatesByFamily[family]);
if (candidate) return candidate;
}
return null;
}

function normalizeTerminalPriorityFamilyStatuses(report = {}, selectedArtifacts = []) {
const rawStatuses = Array.isArray(report.priority_family_statuses)
? report.priority_family_statuses
Expand All @@ -466,12 +481,14 @@ function normalizeTerminalPriorityFamilyStatuses(report = {}, selectedArtifacts
const selectedCount = Number(report.selected_family_counts?.[family]) ||
selectedArtifacts.filter((artifact) => artifact.family === family).length;
const selectedArtifact = selectedArtifacts.find((artifact) => artifact.family === family) || null;
const latestCandidate = latestCandidateFromSelectionReport(report, family) ||
(selectedArtifact ? normalizeSelectionArtifact(selectedArtifact) : null);
byFamily.set(family, {
family,
status: selectedCount > 0 ? 'selected' : (candidateCount > 0 ? 'available' : 'missing'),
candidate_count: candidateCount,
selected_count: selectedCount,
latest_candidate: selectedArtifact ? normalizeSelectionArtifact(selectedArtifact) : null,
latest_candidate: latestCandidate,
selected_artifact: selectedArtifact ? normalizeSelectionArtifact(selectedArtifact) : null,
});
}
Expand Down
38 changes: 38 additions & 0 deletions .github/scripts/weekly_metrics_artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,41 @@ function priorityFamilyStatuses({
});
}

function latestCandidateByFamily(candidates = []) {
const latestByFamily = new Map();
for (const candidate of candidates) {
const family = cleanString(candidate.family);
if (!PRIORITY_METRICS_FAMILIES.includes(family)) continue;
const existing = latestByFamily.get(family);
const candidateTimestamp = Number(candidate.timestamp_ms) || artifactTimestampMs(candidate);
const existingTimestamp = existing ? Number(existing.timestamp_ms) || artifactTimestampMs(existing) : -1;
const candidateId = Number(candidate.id) || 0;
const existingId = existing ? Number(existing.id) || 0 : -1;
if (
!existing ||
candidateTimestamp > existingTimestamp ||
(candidateTimestamp === existingTimestamp && candidateId > existingId)
) {
latestByFamily.set(family, candidate);
}
}
const entries = [];
for (const family of PRIORITY_METRICS_FAMILIES) {
const latestCandidate = latestByFamily.get(family);
if (!latestCandidate) continue;
entries.push([
family,
{
id: latestCandidate.id,
name: latestCandidate.name,
created_at: latestCandidate.created_at,
updated_at: latestCandidate.updated_at,
},
]);
}
return Object.fromEntries(entries);
}

function selectMetricsArtifacts(artifacts = [], options = {}) {
const config = normalizeSelectionOptions(options);
const stats = {
Expand Down Expand Up @@ -261,6 +296,7 @@ function selectMetricsArtifacts(artifacts = [], options = {}) {
...stats,
candidate_family_counts: sortedCountObject(candidateFamilyCounts),
selected_family_counts: sortedCountObject(familyCounts),
latest_candidate_by_family: latestCandidateByFamily(candidates),
missing_priority_families: missingPriorityFamilies(candidateFamilyCounts),
priority_family_statuses: priorityFamilyStatuses({
candidates,
Expand Down Expand Up @@ -302,6 +338,7 @@ function buildSelectionErrorReport(options = {}, error = {}) {
ignored_total_limit_count: 0,
candidate_family_counts: {},
selected_family_counts: {},
latest_candidate_by_family: {},
missing_priority_families: [...PRIORITY_METRICS_FAMILIES],
priority_family_statuses: priorityFamilyStatuses(),
selected_artifacts: [],
Expand Down Expand Up @@ -489,6 +526,7 @@ module.exports = {
collectRepoArtifacts,
formatArtifactTsv,
formatSelectionMarkdown,
latestCandidateByFamily,
missingPriorityFamilies,
normalizeSelectionOptions,
priorityFamilyStatuses,
Expand Down
27 changes: 24 additions & 3 deletions scripts/aggregate_agent_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
),
)
_MAX_PARSE_ERROR_ROWS = 25
_MAX_LEGACY_JSON_FALLBACK_LINES = 5000
_MAX_LEGACY_JSON_FALLBACK_BYTES = 1024 * 1024


@dataclass(frozen=True)
Expand Down Expand Up @@ -180,13 +182,25 @@ def _read_ndjson(files: Iterable[Path]) -> tuple[list[dict[str, Any]], list[Pars
file_entries: list[dict[str, Any]] = []
file_errors: list[ParseErrorDetail] = []
raw_lines_for_fallback: list[str] = []
raw_fallback_bytes = 0
raw_fallback_truncated = False
with handle:
for line_number, line in enumerate(handle, start=1):
raw = line.strip()
if not raw:
continue
if not file_entries:
raw_lines_for_fallback.append(raw)
if not file_entries and not raw_fallback_truncated:
raw_bytes = len(raw.encode("utf-8")) + 1
fallback_within_limit = (
len(raw_lines_for_fallback) < _MAX_LEGACY_JSON_FALLBACK_LINES
and raw_fallback_bytes + raw_bytes <= _MAX_LEGACY_JSON_FALLBACK_BYTES
)
if fallback_within_limit:
raw_fallback_bytes += raw_bytes
raw_lines_for_fallback.append(raw)
else:
raw_fallback_truncated = True
raw_lines_for_fallback = []
try:
parsed = json.loads(raw)
except json.JSONDecodeError:
Expand All @@ -197,7 +211,14 @@ def _read_ndjson(files: Iterable[Path]) -> tuple[list[dict[str, Any]], list[Pars
raw_lines_for_fallback = []
else:
file_errors.append(_parse_error_detail(path, line_number, "non-object-json"))
if file_errors and not file_entries and raw_lines_for_fallback:
if file_errors and not file_entries and raw_fallback_truncated:
file_errors.append(_parse_error_detail(path, None, "legacy-json-fallback-buffer-limit"))
if (
file_errors
and not file_entries
and raw_lines_for_fallback
and not raw_fallback_truncated
):
try:
parsed_file = json.loads("\n".join(raw_lines_for_fallback))
except json.JSONDecodeError:
Expand Down
49 changes: 16 additions & 33 deletions scripts/repo_review_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,11 +444,7 @@ def gap_label(severity: str) -> str:
def build_review_execution(state: dict[str, Any]) -> dict[str, Any]:
repo_path = Path(state["local_path"])
tracked_files = tracked_repo_files(repo_path)
implementation_files = [
path
for path in tracked_files
if is_implementation_file(path)
]
implementation_files = [path for path in tracked_files if is_implementation_file(path)]
test_files = [path for path in tracked_files if is_test_file(path)]
implementation_scan_files = implementation_files[:REVIEW_SCAN_FILE_LIMIT]
test_scan_files = test_files[:REVIEW_SCAN_FILE_LIMIT]
Expand Down Expand Up @@ -499,8 +495,7 @@ def build_review_execution(state: dict[str, Any]) -> dict[str, Any]:
:12
]
design_headings = {
rel_path: markdown_headings(repo_path, rel_path)
for rel_path in state["design_files"][:6]
rel_path: markdown_headings(repo_path, rel_path) for rel_path in state["design_files"][:6]
}
domain_hits = keyword_file_hits(
repo_path,
Expand All @@ -521,12 +516,12 @@ def build_review_execution(state: dict[str, Any]) -> dict[str, Any]:

if not state["design_files"]:
design_severity = "blocks testing"
design_finding = "No tracked design sources were found; the design contract is not reviewable."
else:
design_severity = "none"
design_finding = (
f"Collected {state['design_source_count']} design sources and registry anchor for comparison."
"No tracked design sources were found; the design contract is not reviewable."
)
else:
design_severity = "none"
design_finding = f"Collected {state['design_source_count']} design sources and registry anchor for comparison."
dimensions.append(
{
"id": "design_contract",
Expand Down Expand Up @@ -580,14 +575,10 @@ def build_review_execution(state: dict[str, Any]) -> dict[str, Any]:
test_finding = "No tests or CI workflow files were detected."
elif not smoke_test_files:
test_severity = "material"
test_finding = (
"Tests or workflows exist, but the automated pass did not find smoke/e2e/live readiness markers."
)
test_finding = "Tests or workflows exist, but the automated pass did not find smoke/e2e/live readiness markers."
else:
test_severity = "needs human decision"
test_finding = (
"Test and smoke/integration markers exist; review must verify they prove the intended user journey."
)
test_finding = "Test and smoke/integration markers exist; review must verify they prove the intended user journey."
dimensions.append(
{
"id": "test_and_live_readiness",
Expand Down Expand Up @@ -628,14 +619,10 @@ def build_review_execution(state: dict[str, Any]) -> dict[str, Any]:

if state["issue_draft_count"] or state["archive_candidate_count"]:
issue_severity = "needs human decision"
issue_finding = (
"Draft inputs exist; approve only after checking them against the executed review evidence."
)
issue_finding = "Draft inputs exist; approve only after checking them against the executed review evidence."
elif state["remote_open_issue_count"]:
issue_severity = "needs human decision"
issue_finding = (
"No local/archive candidates are queued, but remote open issues need reconciliation before drafting more."
)
issue_finding = "No local/archive candidates are queued, but remote open issues need reconciliation before drafting more."
else:
issue_severity = "needs human decision"
issue_finding = (
Expand Down Expand Up @@ -1367,9 +1354,7 @@ def write_repo_artifacts(output_dir: Path, state: dict[str, Any], max_drafts: in
"",
]
)
(repo_dir / "review-execution.md").write_text(
"\n".join(execution_lines), encoding="utf-8"
)
(repo_dir / "review-execution.md").write_text("\n".join(execution_lines), encoding="utf-8")

draft_lines = [
f"# Issue Drafts: {state['repo']}",
Expand Down Expand Up @@ -1432,19 +1417,15 @@ def write_packet(output_dir: Path, states: list[dict[str, Any]], generated_on: s
ignored = [state for state in states if state["status"] == "ignored"]
review_pending = [state for state in active if state["review_status"] == PENDING_REVIEW_STATUS]
blocked = [
state
for state in active
if str(state["review_execution"]["status"]).startswith("blocked")
state for state in active if str(state["review_execution"]["status"]).startswith("blocked")
]
issue_candidate_repos = [
state for state in active if state["issue_queue_status"] == "draft candidates present"
]
executed_reviews = [
state for state in active if state["review_execution"]["status"] == "executed"
]
automated_gap_repos = [
state for state in active if state["review_execution"]["gap_count"] > 0
]
automated_gap_repos = [state for state in active if state["review_execution"]["gap_count"] > 0]

lines = [
f"# Weekly Design Review Decision Packet - {generated_on}",
Expand Down Expand Up @@ -1481,7 +1462,9 @@ def write_packet(output_dir: Path, states: list[dict[str, Any]], generated_on: s
"approve/edit/defer issue drafts."
)
elif str(state["review_execution"]["status"]).startswith("blocked"):
human_action = "resolve the blocker, rerun review execution, then queue the human decision."
human_action = (
"resolve the blocker, rerun review execution, then queue the human decision."
)
else:
human_action = "conduct the standardized review, then approve/edit/defer issue drafts."
lines.extend(
Expand Down
Loading
Loading