Files
xjx c895301268 Implement engineering speedup architecture (#16)
Land the B0/M0-M5/E0-E4/P0-P3 engineering-speedup control plane, including exact Swift caching, measured engineering metrics, canonical Rust identity validation, deterministic test isolation, proof fallback/promotion, and supporting architecture splits.\n\nRelease validation and dispatch are intentionally deferred to the next release.
2026-08-12 21:25:00 +08:00

580 lines
25 KiB
Python
Executable File

#!/usr/bin/env python3
"""Emit bounded, content-free engineering feedback and CI timing metrics."""
from __future__ import annotations
import argparse
from datetime import datetime
import json
import pathlib
import re
import subprocess
import sys
from typing import Any, NoReturn
from development_domains import load_manifest, path_matches
SCHEMA_VERSION = "apc.engineering-metrics.v2"
COORDINATION_SCHEMA_VERSION = "apc.engineering-coordination.v1"
MAX_INPUT_BYTES = 4 * 1024 * 1024
MAX_CHANGED_PATHS = 4096
MAX_JOBS = 256
MAX_STEPS_PER_JOB = 128
MAX_CACHES = 256
FALLBACK_CATEGORIES = {
"none",
"not-applicable",
"control-plane-change",
"expired",
"ambiguous",
"tree-mismatch",
"missing-artifact",
"unavailable",
}
HOTSPOTS = {
"app-store": ("apps/macos/Sources/AgentPetCompanion/App/AppStore.swift",),
# Count the former shared monoliths, not their feature-owned module trees.
# Otherwise a successful split would still look like hotspot contention.
"connections-monolith": ("crates/petcore/src/connections.rs",),
"rpc-monolith": ("crates/petcore/src/rpc.rs",),
"storage-monolith": ("crates/petcore/src/db.rs",),
"global-localization": (
"apps/macos/Sources/AgentPetCompanion/App/Localization.swift",
"apps/macos/Sources/AgentPetCompanion/Resources/Localizable.xcstrings",
"apps/macos/Sources/AgentPetCompanion/Resources/en.lproj/Localizable.strings",
"apps/macos/Sources/AgentPetCompanion/Resources/zh-Hans.lproj/Localizable.strings",
),
"root-changelog": ("CHANGELOG.md",),
}
COORDINATION_MARKER = re.compile(
r"<!-- apc-engineering-coordination-v1 (?P<payload>\{[^\r\n]{1,2048}\}) -->"
)
COORDINATION_FIELDS = (
"claim_overlap_rejections",
"merge_tree_conflicts",
"train_conflict_resolutions",
)
# GitHub's jobs API omits `needs`, so this small public display-name graph is
# the versioned definition used for the DAG critical-path calculation.
CI_JOB_DEPENDENCIES: dict[str, tuple[str, ...]] = {
"Promote trusted PR proof to main": ("Classify change once",),
"Static source contracts": ("Classify change once", "Promote trusted PR proof to main"),
"Portable source and integration contracts": ("Classify change once", "Promote trusted PR proof to main"),
"macOS build and lifecycle contracts": ("Classify change once", "Promote trusted PR proof to main"),
"Prime portable Cargo sources": ("Classify change once", "Promote trusted PR proof to main"),
"Rust formatting and linting": ("Classify change once", "Promote trusted PR proof to main", "Prime portable Cargo sources"),
"Link portable Rust test binaries once": ("Classify change once", "Promote trusted PR proof to main", "Prime portable Cargo sources"),
"Verify every Rust test shard": (
"Classify change once",
"Promote trusted PR proof to main",
"Rust tests (core)",
"Rust tests (integration-a)",
"Rust tests (integration-b)",
"Rust tests (integration-c)",
"Rust tests (integration-d)",
),
"macOS Rust platform semantics": ("Classify change once", "Promote trusted PR proof to main"),
"Swift and interaction proof": ("Classify change once", "Promote trusted PR proof to main"),
"Bounded event-storm stress": (
"Classify change once",
"Promote trusted PR proof to main",
"Prime portable Cargo sources",
"Link portable Rust test binaries once",
),
"Offline overlay contracts": ("Classify change once", "Promote trusted PR proof to main", "Swift and interaction proof"),
"Packaged development App proof": ("Classify change once", "Promote trusted PR proof to main", "Swift and interaction proof"),
}
CANDIDATE_DEPENDENCIES = (
"Static source contracts",
"Portable source and integration contracts",
"macOS build and lifecycle contracts",
"Prime portable Cargo sources",
"Rust formatting and linting",
"Link portable Rust test binaries once",
"Verify every Rust test shard",
"macOS Rust platform semantics",
"Swift and interaction proof",
"Bounded event-storm stress",
"Offline overlay contracts",
"Packaged development App proof",
)
CI_JOB_DEPENDENCIES["Create promotable PR source proof"] = CANDIDATE_DEPENDENCIES
CI_JOB_DEPENDENCIES["Bind release source proof to main commit"] = (
*CANDIDATE_DEPENDENCIES,
"Create promotable PR source proof",
)
def fail(message: str) -> NoReturn:
raise ValueError(message)
def read_json(path: pathlib.Path) -> Any:
metadata = path.lstat()
if not path.is_file() or path.is_symlink() or metadata.st_size > MAX_INPUT_BYTES:
fail(f"metrics input must be a bounded regular file: {path.name}")
return json.loads(path.read_text(encoding="utf-8"))
def changed_paths(root: pathlib.Path, base_ref: str) -> list[str]:
result = subprocess.run(
["git", "diff", "--name-only", "--no-renames", f"{base_ref}...HEAD", "--"],
cwd=root,
check=True,
stdout=subprocess.PIPE,
text=True,
)
paths = sorted({line for line in result.stdout.splitlines() if line})
if len(paths) > MAX_CHANGED_PATHS:
fail("changed-path inventory exceeds its bound")
if any(path.startswith("/") or ".." in pathlib.PurePosixPath(path).parts for path in paths):
fail("changed-path inventory contains an unsafe path")
return paths
def parse_timestamp(value: Any) -> datetime | None:
if not isinstance(value, str) or not value:
return None
return datetime.fromisoformat(value.replace("Z", "+00:00"))
def job_dependencies(name: str) -> tuple[str, ...]:
if name.startswith("Rust tests (") and name.endswith(")"):
return (
"Classify change once",
"Promote trusted PR proof to main",
"Link portable Rust test binaries once",
)
return CI_JOB_DEPENDENCIES.get(name, ())
def dag_critical_path(rows: list[dict[str, Any]]) -> int:
by_name = {row["name"]: row for row in rows}
if len(by_name) != len(rows):
fail("jobs input contains duplicate display names")
memo: dict[str, int] = {}
active: set[str] = set()
def visit(name: str) -> int:
if name in memo:
return memo[name]
if name in active:
fail("CI job dependency graph contains a cycle")
active.add(name)
parents = [
visit(parent) for parent in job_dependencies(name) if parent in by_name
]
value = int(by_name[name]["duration_seconds"]) + (
max(parents) if parents else 0
)
active.remove(name)
memo[name] = value
return value
return max((visit(name) for name in by_name), default=0)
def peak_platform_concurrency(
intervals: list[tuple[datetime, datetime]],
) -> int:
events = [
event
for started, completed in intervals
for event in ((started, 1), (completed, -1))
]
active = 0
peak = 0
# End events sort first at identical timestamps so adjacent jobs do not
# look concurrent merely because GitHub rounded their boundary equally.
for _, delta in sorted(events, key=lambda event: (event[0], event[1])):
active += delta
peak = max(peak, active)
return peak
def job_metrics(payload: Any, run_created_at: datetime | None = None) -> dict[str, Any]:
jobs = payload.get("jobs") if isinstance(payload, dict) else payload
if not isinstance(jobs, list) or len(jobs) > MAX_JOBS:
fail("jobs input must contain a bounded jobs array")
rows: list[dict[str, Any]] = []
macos_seconds = 0
linux_seconds = 0
starts: list[datetime] = []
ends: list[datetime] = []
cache_restore_seconds = 0
cache_save_seconds = 0
macos_intervals: list[tuple[datetime, datetime]] = []
for job in jobs:
if not isinstance(job, dict):
fail("jobs input contains a non-object")
name = job.get("name")
started = parse_timestamp(job.get("startedAt") or job.get("started_at"))
completed = parse_timestamp(job.get("completedAt") or job.get("completed_at"))
labels = job.get("labels", [])
runner = str(job.get("runnerName") or job.get("runner_name") or "")
if not isinstance(name, str) or len(name) > 160:
fail("job name is invalid")
duration = 0
if started is not None and completed is not None:
duration = max(0, int((completed - started).total_seconds()))
starts.append(started)
ends.append(completed)
identity = " ".join(str(label) for label in labels) + " " + runner
platform = "macos" if "macos" in identity.lower() else "linux" if "ubuntu" in identity.lower() or "linux" in identity.lower() else "unknown"
if platform == "macos":
macos_seconds += duration
if started is not None and completed is not None:
macos_intervals.append((started, completed))
elif platform == "linux":
linux_seconds += duration
steps = job.get("steps", [])
if not isinstance(steps, list) or len(steps) > MAX_STEPS_PER_JOB:
fail("job steps must be a bounded array")
for step in steps:
if not isinstance(step, dict):
fail("job steps contain a non-object")
step_name = step.get("name")
if not isinstance(step_name, str) or len(step_name) > 160:
fail("job step name is invalid")
normalized_name = step_name.lower()
if "cache" not in normalized_name:
continue
step_started = parse_timestamp(step.get("startedAt") or step.get("started_at"))
step_completed = parse_timestamp(
step.get("completedAt") or step.get("completed_at")
)
step_seconds = (
max(0, int((step_completed - step_started).total_seconds()))
if step_started is not None and step_completed is not None
else 0
)
if normalized_name.startswith("post ") or "save cache" in normalized_name:
cache_save_seconds += step_seconds
else:
cache_restore_seconds += step_seconds
rows.append({"name": name, "platform": platform, "duration_seconds": duration, "conclusion": job.get("conclusion")})
rows.sort(key=lambda row: row["name"])
wall = int((max(ends) - min(starts)).total_seconds()) if starts and ends else 0
queue = (
max(0, int((min(starts) - run_created_at).total_seconds()))
if starts and run_created_at is not None
else None
)
return {
"jobs": rows,
"queue_seconds": queue,
"execution_seconds": sum(int(row["duration_seconds"]) for row in rows),
"critical_path_seconds": dag_critical_path(rows),
"workflow_wall_seconds": max(0, wall),
"macos_job_seconds": macos_seconds,
"linux_job_seconds": linux_seconds,
"peak_macos_concurrency": peak_platform_concurrency(macos_intervals),
"cache_restore_seconds": cache_restore_seconds,
"cache_save_seconds": cache_save_seconds,
}
def cache_metrics(payload: Any, observed_at: datetime) -> dict[str, Any]:
caches = payload.get("actions_caches") if isinstance(payload, dict) else None
if not isinstance(caches, list) or len(caches) > MAX_CACHES:
fail("cache input must contain a bounded actions_caches array")
total_size = 0
churn_size = 0
for cache in caches:
if not isinstance(cache, dict):
fail("cache input contains a non-object")
size = cache.get("size_in_bytes")
created = parse_timestamp(cache.get("created_at"))
if not isinstance(size, int) or size < 0:
fail("cache size is invalid")
total_size += size
if created is not None and 0 <= (observed_at - created).total_seconds() <= 86_400:
churn_size += size
return {
"listed_cache_count": len(caches),
"listed_size_bytes": total_size,
"created_last_24h_bytes": churn_size,
}
def lifecycle_metrics(values: list[str | None]) -> dict[str, int | None]:
created, ready, merged = (parse_timestamp(value) for value in values)
return {
"task_to_ready_seconds": (
max(0, int((ready - created).total_seconds()))
if created is not None and ready is not None
else None
),
"ready_to_merged_seconds": (
max(0, int((merged - ready).total_seconds()))
if ready is not None and merged is not None
else None
),
}
def coordination_marker(body: Any) -> dict[str, Any] | None:
if not isinstance(body, str) or len(body.encode("utf-8")) > 256 * 1024:
return None
matches = list(COORDINATION_MARKER.finditer(body))
if not matches:
return None
if len(matches) != 1:
fail("pull request body contains duplicate engineering coordination markers")
payload = json.loads(matches[0].group("payload"))
expected = {"schema_version", "task_created_at", *COORDINATION_FIELDS}
if not isinstance(payload, dict) or set(payload) != expected:
fail("engineering coordination marker field inventory is invalid")
if payload["schema_version"] != COORDINATION_SCHEMA_VERSION:
fail("engineering coordination marker schema is unsupported")
if parse_timestamp(payload["task_created_at"]) is None:
fail("engineering coordination task timestamp is invalid")
for field in COORDINATION_FIELDS:
if not isinstance(payload[field], int) or not 0 <= payload[field] <= 1_000_000:
fail("engineering coordination count is invalid")
return payload
def github_pr_metrics(
pull_request: Any,
timeline: Any,
) -> tuple[dict[str, int | None], dict[str, int | None]]:
if not isinstance(pull_request, dict):
fail("pull-request input must contain one object")
if not isinstance(timeline, list) or len(timeline) > 2048:
fail("pull-request timeline must be a bounded array")
marker = coordination_marker(pull_request.get("body"))
task_created_value = marker["task_created_at"] if marker else pull_request.get(
"created_at"
)
task_created = parse_timestamp(task_created_value)
pull_request_created = parse_timestamp(pull_request.get("created_at"))
merged = parse_timestamp(pull_request.get("merged_at"))
ready_events: list[datetime] = []
converted_events: list[datetime] = []
for event in timeline:
if not isinstance(event, dict):
fail("pull-request timeline contains a non-object")
timestamp = parse_timestamp(event.get("created_at"))
if timestamp is None:
continue
if event.get("event") == "ready_for_review":
ready_events.append(timestamp)
elif event.get("event") == "convert_to_draft":
converted_events.append(timestamp)
if ready_events:
ready = min(ready_events)
elif pull_request_created is not None and (
pull_request.get("draft") is False or converted_events
):
# With no ready event, the PR was born ready. A later draft conversion
# does not erase that first-ready timestamp.
ready = pull_request_created
else:
ready = None
lifecycle = lifecycle_metrics(
[
task_created.isoformat() if task_created else None,
ready.isoformat() if ready else None,
merged.isoformat() if merged else None,
]
)
coordination = {
field: marker[field] if marker else None for field in COORDINATION_FIELDS
}
return lifecycle, coordination
def change_metrics(root: pathlib.Path, paths: list[str]) -> dict[str, Any]:
manifest = load_manifest(root)
domains: dict[str, int] = {}
amber: set[str] = set()
red: set[str] = set()
for path in paths:
for domain in manifest.domains:
if any(path_matches(path, pattern) for pattern in domain.owned_paths):
domains[domain.id] = domains.get(domain.id, 0) + 1
for shared in domain.shared_paths:
if path_matches(path, shared.path):
(red if shared.risk == "red" else amber).add(path)
hotspots = {
name: sum(any(path_matches(path, pattern) for pattern in patterns) for path in paths)
for name, patterns in HOTSPOTS.items()
}
return {
"changed_path_count": len(paths),
"domain_path_counts": dict(sorted(domains.items())),
"amber_path_count": len(amber),
"red_path_count": len(red),
"hotspot_path_counts": hotspots,
}
def render_summary(payload: dict[str, Any]) -> str:
change = payload["change"]
timing = payload.get("timing")
proof = payload["proof"]
coordination = payload["coordination"]
lifecycle = payload["lifecycle"]
rendered_coordination = {
field: coordination[field] if coordination[field] is not None else "n/a"
for field in COORDINATION_FIELDS
}
lines = [
"### Engineering feedback metrics",
"",
"| metric | value |",
"|---|---:|",
f"| changed paths | {change['changed_path_count']} |",
f"| Amber paths | {change['amber_path_count']} |",
f"| Red paths | {change['red_path_count']} |",
f"| proof outcome | {proof['outcome']} |",
f"| promotion fallback | {proof['fallback_category']} |",
f"| claim-overlap rejections | {rendered_coordination['claim_overlap_rejections']} |",
f"| merge-tree conflicts | {rendered_coordination['merge_tree_conflicts']} |",
f"| train conflict resolutions | {rendered_coordination['train_conflict_resolutions']} |",
f"| task-to-ready seconds | {lifecycle['task_to_ready_seconds'] if lifecycle['task_to_ready_seconds'] is not None else 'n/a'} |",
f"| ready-to-merged seconds | {lifecycle['ready_to_merged_seconds'] if lifecycle['ready_to_merged_seconds'] is not None else 'n/a'} |",
]
for name, observation in payload.get("cache_exact_hits", {}).items():
lines.append(f"| exact cache `{name}` | {observation} |")
if timing is not None:
lines.extend(
[
f"| queue seconds | {timing['queue_seconds'] if timing['queue_seconds'] is not None else 'n/a'} |",
f"| execution seconds | {timing['execution_seconds']} |",
f"| workflow wall seconds | {timing['workflow_wall_seconds']} |",
f"| DAG critical path seconds | {timing['critical_path_seconds']} |",
f"| macOS job-minutes | {timing['macos_job_seconds'] / 60:.2f} |",
f"| Linux job-minutes | {timing['linux_job_seconds'] / 60:.2f} |",
f"| peak concurrent macOS jobs | {timing['peak_macos_concurrency']} |",
f"| cache restore seconds | {timing['cache_restore_seconds']} |",
f"| cache save seconds | {timing['cache_save_seconds']} |",
]
)
cache_inventory = payload.get("cache_inventory")
if cache_inventory is not None:
lines.extend(
[
f"| listed cache count | {cache_inventory['listed_cache_count']} |",
f"| listed cache MiB | {cache_inventory['listed_size_bytes'] / 1_048_576:.2f} |",
f"| cache churn MiB (24h) | {cache_inventory['created_last_24h_bytes'] / 1_048_576:.2f} |",
]
)
lines.extend(["", "Domain and hotspot counts contain repository-relative metadata only; source contents are never collected.", ""])
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=pathlib.Path, default=pathlib.Path.cwd())
parser.add_argument("--base-ref", required=True)
parser.add_argument("--jobs-json", type=pathlib.Path)
parser.add_argument("--cache-json", type=pathlib.Path)
parser.add_argument("--pull-request-json", type=pathlib.Path)
parser.add_argument("--pull-request-timeline-json", type=pathlib.Path)
parser.add_argument("--run-created-at")
parser.add_argument("--observed-at")
parser.add_argument("--proof-outcome", choices=("promoted", "full-validation", "not-applicable"), default="not-applicable")
parser.add_argument("--fallback-category", default="not-applicable")
parser.add_argument("--cache-observation", action="append", default=[])
parser.add_argument("--claim-overlap-rejections", type=int)
parser.add_argument("--merge-tree-conflicts", type=int)
parser.add_argument("--train-conflict-resolutions", type=int)
parser.add_argument("--task-created-at")
parser.add_argument("--ready-at")
parser.add_argument("--merged-at")
parser.add_argument("--output", type=pathlib.Path, required=True)
parser.add_argument("--summary", type=pathlib.Path)
args = parser.parse_args()
try:
root = args.root.resolve()
coordination = {
"claim_overlap_rejections": args.claim_overlap_rejections,
"merge_tree_conflicts": args.merge_tree_conflicts,
"train_conflict_resolutions": args.train_conflict_resolutions,
}
lifecycle = lifecycle_metrics(
[args.task_created_at, args.ready_at, args.merged_at]
)
if (args.pull_request_json is None) != (
args.pull_request_timeline_json is None
):
fail("pull-request metadata and timeline inputs must be supplied together")
if args.pull_request_json is not None:
github_lifecycle, github_coordination = github_pr_metrics(
read_json(args.pull_request_json.resolve()),
read_json(args.pull_request_timeline_json.resolve()),
)
lifecycle = {
key: value if value is not None else github_lifecycle[key]
for key, value in lifecycle.items()
}
coordination = {
key: value if value is not None else github_coordination[key]
for key, value in coordination.items()
}
payload: dict[str, Any] = {
"schema_version": SCHEMA_VERSION,
"base_ref": args.base_ref,
"change": change_metrics(root, changed_paths(root, args.base_ref)),
"proof": {
"outcome": args.proof_outcome,
"fallback_category": args.fallback_category,
},
"coordination": coordination,
"lifecycle": lifecycle,
"definitions": {
"critical_path": "longest completed job-duration path in the versioned CI needs graph",
"task_created_at": "development-flow claim timestamp, falling back to pull-request creation",
"ready_at": "first ready-for-review timestamp, or creation when born ready",
"coordination": "cumulative content-free development-flow counters snapshotted into the pull request",
},
}
if args.fallback_category not in FALLBACK_CATEGORIES:
fail("fallback category is not closed")
observed_coordination = [
value for value in coordination.values() if value is not None
]
if any(not isinstance(value, int) or value < 0 for value in observed_coordination):
fail("coordination counts must not be negative")
observations: dict[str, str] = {}
for observation in args.cache_observation:
name, separator, value = observation.partition("=")
if (
not separator
or not name.replace("-", "").replace("_", "").isalnum()
or value not in {"true", "false", "skipped", "unavailable"}
or name in observations
):
fail("cache observation must be a unique name=true|false|skipped|unavailable")
observations[name] = value
payload["cache_exact_hits"] = dict(sorted(observations.items()))
if args.jobs_json is not None:
payload["timing"] = job_metrics(
read_json(args.jobs_json.resolve()), parse_timestamp(args.run_created_at)
)
if args.cache_json is not None:
observed_at = parse_timestamp(args.observed_at)
if observed_at is None:
fail("--cache-json requires --observed-at")
payload["cache_inventory"] = cache_metrics(
read_json(args.cache_json.resolve()), observed_at
)
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
summary = render_summary(payload)
if args.summary is not None:
args.summary.write_text(summary, encoding="utf-8")
print(summary, end="")
except (OSError, ValueError, json.JSONDecodeError, subprocess.CalledProcessError) as error:
print(f"engineering metrics: {error}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())