返回 CodeWhale
check-runtime-contract-budget.py
根目录 / scripts / check-runtime-contract-budget.py
1 #!/usr/bin/env python3
2 """Enforce one-way ceilings on Codewhale's provider-free runtime contract.
3
4 The default invocation runs ``measure-runtime-contract.py`` with Cargo forced
5 offline. Pass ``--receipt`` to check an existing JSON receipt without compiling,
6 which also keeps this checker's unit tests hermetic.
7
8 Usage:
9 python3 scripts/check-runtime-contract-budget.py
10 python3 scripts/check-runtime-contract-budget.py --receipt receipt.json
11 python3 scripts/check-runtime-contract-budget.py --update
12 """
13
14 from __future__ import annotations
15
16 import argparse
17 import copy
18 import hashlib
19 import json
20 import os
21 import re
22 import shlex
23 import stat
24 import subprocess
25 import sys
26 import tempfile
27 from pathlib import Path
28 from typing import Any, Sequence
29
30 REPO_ROOT = Path(__file__).resolve().parent.parent
31 BUDGET_PATH = REPO_ROOT / "scripts" / "runtime-contract-budget.json"
32 MEASURE_SCRIPT = REPO_ROOT / "scripts" / "measure-runtime-contract.py"
33 RECEIPT_KIND = "codewhale.runtime_contract_receipt"
34 BUDGET_KIND = "codewhale.runtime_contract_budget"
35 SCHEMA_VERSION = 1
36 REPRESENTATIVE_FIXTURE_ID = "representative-v1"
37 TOOL_SURFACE_PROFILE = "production-default-builtins-no-mcp-no-host-interpreters-v1"
38
39 MetricPath = tuple[str, ...]
40 MetricResult = tuple[str, str, int, int]
41
42 VISIBLE_MODES = (("plan", "Plan"), ("act", "Act"), ("operate", "Operate"))
43 REPRESENTATIVE_STAGES = (
44 ("base", "base"),
45 ("project", "project-authority"),
46 ("instructions", "configured-instructions"),
47 ("skill", "skill"),
48 ("memory", "memory"),
49 ("goal", "goal"),
50 ("handoff", "handoff"),
51 )
52 TOOL_SURFACES = (("full", "full"), ("active", "active"))
53
54 METRICS: tuple[tuple[MetricPath, str], ...] = (
55 *(
56 (("system_prompt", "modes", mode, field), f"{label} {description}")
57 for mode, label in VISIBLE_MODES
58 for field, description in (
59 ("system_prompt_bytes", "system-prompt bytes"),
60 ("system_prompt_tokens_est", "system-prompt estimated tokens"),
61 ("system_prompt_blocks", "system-prompt blocks"),
62 ("mode_instructions_bytes", "mode-instruction bytes"),
63 ("mode_instructions_tokens_est", "mode-instruction estimated tokens"),
64 )
65 ),
66 *(
67 (
68 ("representative_context", "stages", stage, "bytes"),
69 f"representative {label} stage bytes",
70 )
71 for stage, label in REPRESENTATIVE_STAGES
72 ),
73 *(
74 (
75 ("representative_context", "stages", stage, "delta_bytes"),
76 f"representative {label} stage delta bytes",
77 )
78 for stage, label in REPRESENTATIVE_STAGES[1:]
79 ),
80 (
81 ("representative_context", "total_bytes"),
82 "representative total bytes",
83 ),
84 (
85 ("representative_context", "total_tokens_est"),
86 "representative estimated tokens",
87 ),
88 (
89 ("representative_context", "system_prompt_blocks"),
90 "representative system-prompt blocks",
91 ),
92 *(
93 (
94 ("tool_catalog", "modes", mode, surface, field),
95 f"{label} {surface_label} {description}",
96 )
97 for mode, label in VISIBLE_MODES
98 for surface, surface_label in TOOL_SURFACES
99 for field, description in (
100 ("tools", "tool count"),
101 ("bytes", "tool-schema bytes"),
102 ("tokens_est", "tool-schema estimated tokens"),
103 )
104 ),
105 (
106 ("skill_discovery", "first_delta", "root_discovery_calls"),
107 "first unchanged-turn root discovery calls",
108 ),
109 (
110 ("skill_discovery", "first_delta", "directories_visited"),
111 "first unchanged-turn directories visited",
112 ),
113 (
114 ("skill_discovery", "first_delta", "skill_md_read_attempts"),
115 "first unchanged-turn SKILL.md read attempts",
116 ),
117 (
118 ("skill_discovery", "second_delta", "root_discovery_calls"),
119 "second unchanged-turn root discovery calls",
120 ),
121 (
122 ("skill_discovery", "second_delta", "directories_visited"),
123 "second unchanged-turn directories visited",
124 ),
125 (
126 ("skill_discovery", "second_delta", "skill_md_read_attempts"),
127 "second unchanged-turn SKILL.md read attempts",
128 ),
129 )
130
131 IDENTITIES: tuple[tuple[MetricPath, str], ...] = (
132 (("tool_catalog", "surface_profile"), "tool surface profile"),
133 *(
134 (
135 ("tool_catalog", "modes", mode, surface, field),
136 f"{label} {surface_label} tool {description}",
137 )
138 for mode, label in VISIBLE_MODES
139 for surface, surface_label in TOOL_SURFACES
140 for field, description in (
141 ("tool_names", "names"),
142 ("identity_sha256", "identity digest"),
143 )
144 ),
145 *(
146 (
147 ("representative_context", "stages", stage, "identity_sha256"),
148 f"representative {label} stage identity digest",
149 )
150 for stage, label in REPRESENTATIVE_STAGES
151 ),
152 )
153
154
155 class RuntimeContractError(ValueError):
156 """A receipt or budget is missing a required, well-typed metric."""
157
158
159 def load_json(path: Path, kind: str) -> dict[str, Any]:
160 try:
161 document = json.loads(path.read_text(encoding="utf-8"))
162 except FileNotFoundError as error:
163 raise RuntimeContractError(f"missing {kind}: {path}") from error
164 except (OSError, json.JSONDecodeError) as error:
165 raise RuntimeContractError(f"invalid {kind} {path}: {error}") from error
166 if not isinstance(document, dict):
167 raise RuntimeContractError(f"invalid {kind} {path}: top level must be an object")
168 return document
169
170
171 def validate_document(
172 document: dict[str, Any], expected_kind: str, source: str
173 ) -> None:
174 actual_kind = document.get("document_kind")
175 if actual_kind != expected_kind:
176 raise RuntimeContractError(
177 f"{source} document_kind must be `{expected_kind}`, got {actual_kind!r}"
178 )
179 version = document.get("schema_version")
180 if (
181 isinstance(version, bool)
182 or not isinstance(version, int)
183 or version != SCHEMA_VERSION
184 ):
185 raise RuntimeContractError(
186 f"{source} schema_version must be {SCHEMA_VERSION}, got {version!r}"
187 )
188
189
190 def required_value(document: dict[str, Any], path: MetricPath, kind: str) -> Any:
191 value: Any = document
192 dotted = ".".join(path)
193 for part in path:
194 if not isinstance(value, dict) or part not in value:
195 raise RuntimeContractError(f"{kind} is missing required field `{dotted}`")
196 value = value[part]
197 return value
198
199
200 def tool_identity_digest(names: list[str]) -> str:
201 return hashlib.sha256("\0".join(names).encode("utf-8")).hexdigest()
202
203
204 def validate_identity_structure(document: dict[str, Any], kind: str) -> None:
205 profile = required_value(document, ("tool_catalog", "surface_profile"), kind)
206 if profile != TOOL_SURFACE_PROFILE:
207 raise RuntimeContractError(
208 f"{kind} tool surface_profile must be `{TOOL_SURFACE_PROFILE}`, "
209 f"got {profile!r}"
210 )
211
212 for mode, _label in VISIBLE_MODES:
213 for surface, _surface_label in TOOL_SURFACES:
214 base = ("tool_catalog", "modes", mode, surface)
215 names = required_value(document, (*base, "tool_names"), kind)
216 dotted_names = ".".join((*base, "tool_names"))
217 if (
218 not isinstance(names, list)
219 or any(not isinstance(name, str) or not name for name in names)
220 or names != sorted(set(names))
221 ):
222 raise RuntimeContractError(
223 f"{kind} field `{dotted_names}` must be sorted unique non-empty strings"
224 )
225 count = metric_value(document, (*base, "tools"), kind)
226 if count != len(names):
227 raise RuntimeContractError(
228 f"{kind} metric `{'.'.join((*base, 'tools'))}` must equal the "
229 f"owned tool_names length ({len(names)})"
230 )
231 digest = required_value(document, (*base, "identity_sha256"), kind)
232 expected = tool_identity_digest(names)
233 if digest != expected:
234 raise RuntimeContractError(
235 f"{kind} field `{'.'.join((*base, 'identity_sha256'))}` must "
236 "match the owned sorted tool_names"
237 )
238
239 for stage, _label in REPRESENTATIVE_STAGES:
240 path = ("representative_context", "stages", stage, "identity_sha256")
241 digest = required_value(document, path, kind)
242 if not isinstance(digest, str) or re.fullmatch(r"[0-9a-f]{64}", digest) is None:
243 raise RuntimeContractError(
244 f"{kind} field `{'.'.join(path)}` must be a lowercase SHA-256 digest"
245 )
246
247
248 def validate_receipt(receipt: dict[str, Any]) -> None:
249 validate_document(receipt, RECEIPT_KIND, "receipt")
250 skill_discovery = receipt.get("skill_discovery")
251 identical = (
252 skill_discovery.get("prompts_byte_identical")
253 if isinstance(skill_discovery, dict)
254 else None
255 )
256 if identical is not True:
257 raise RuntimeContractError(
258 "receipt metric `skill_discovery.prompts_byte_identical` must be true"
259 )
260 representative = receipt.get("representative_context")
261 fixture_id = (
262 representative.get("fixture_id")
263 if isinstance(representative, dict)
264 else None
265 )
266 if fixture_id != REPRESENTATIVE_FIXTURE_ID:
267 raise RuntimeContractError(
268 "receipt metric `representative_context.fixture_id` must be "
269 f"`{REPRESENTATIVE_FIXTURE_ID}`, got {fixture_id!r}"
270 )
271 representative_identical = representative.get("prompts_byte_identical")
272 if representative_identical is not True:
273 raise RuntimeContractError(
274 "receipt metric `representative_context.prompts_byte_identical` must be true"
275 )
276 validate_identity_structure(receipt, "receipt")
277
278
279 def validate_budget(budget: dict[str, Any]) -> None:
280 validate_document(budget, BUDGET_KIND, "budget")
281 representative = budget.get("representative_context")
282 fixture_id = (
283 representative.get("fixture_id")
284 if isinstance(representative, dict)
285 else None
286 )
287 if fixture_id != REPRESENTATIVE_FIXTURE_ID:
288 raise RuntimeContractError(
289 "budget metric `representative_context.fixture_id` must be "
290 f"`{REPRESENTATIVE_FIXTURE_ID}`, got {fixture_id!r}"
291 )
292 validate_identity_structure(budget, "budget")
293
294
295 def metric_value(document: dict[str, Any], path: MetricPath, kind: str) -> int:
296 value = required_value(document, path, kind)
297 dotted = ".".join(path)
298 if isinstance(value, bool) or not isinstance(value, int) or value < 0:
299 raise RuntimeContractError(
300 f"{kind} metric `{dotted}` must be a non-negative integer"
301 )
302 return value
303
304
305 def compare(
306 receipt: dict[str, Any], budget: dict[str, Any]
307 ) -> tuple[list[MetricResult], list[MetricResult]]:
308 """Return (increases, decreases) as path/label/current/ceiling tuples."""
309 validate_receipt(receipt)
310 validate_budget(budget)
311 for path, label in IDENTITIES:
312 receipt_value = required_value(receipt, path, "receipt")
313 budget_value = required_value(budget, path, "budget")
314 if receipt_value != budget_value:
315 detail = ""
316 if isinstance(receipt_value, list) and isinstance(budget_value, list):
317 added = [str(item) for item in receipt_value if item not in budget_value]
318 removed = [
319 str(item) for item in budget_value if item not in receipt_value
320 ]
321 detail = f" (added={added} removed={removed})"
322 raise RuntimeContractError(
323 f"identity changed for {label} [`{'.'.join(path)}`]{detail}"
324 )
325 increases: list[MetricResult] = []
326 decreases: list[MetricResult] = []
327 for path, label in METRICS:
328 current = metric_value(receipt, path, "receipt")
329 ceiling = metric_value(budget, path, "budget")
330 result = (".".join(path), label, current, ceiling)
331 if current > ceiling:
332 increases.append(result)
333 elif current < ceiling:
334 decreases.append(result)
335 return increases, decreases
336
337
338 def set_path_value(document: dict[str, Any], path: MetricPath, value: Any) -> None:
339 target = document
340 for part in path[:-1]:
341 target = target.setdefault(part, {})
342 target[path[-1]] = value
343
344
345 def budget_from_receipt(receipt: dict[str, Any]) -> dict[str, Any]:
346 validate_receipt(receipt)
347 budget: dict[str, Any] = {
348 "_comment": (
349 "One-way numeric ceilings and exact structural identities for the "
350 "provider-free runtime contract. Decreases pass; increases or identity "
351 "changes fail. Lock in decreases with: python3 "
352 "scripts/check-runtime-contract-budget.py --update"
353 ),
354 "document_kind": BUDGET_KIND,
355 "schema_version": SCHEMA_VERSION,
356 "representative_context": {
357 "fixture_id": REPRESENTATIVE_FIXTURE_ID,
358 },
359 }
360 for path, _label in METRICS:
361 set_path_value(budget, path, metric_value(receipt, path, "receipt"))
362 for path, _label in IDENTITIES:
363 set_path_value(
364 budget,
365 path,
366 copy.deepcopy(required_value(receipt, path, "receipt")),
367 )
368 return budget
369
370
371 def write_budget_atomic(path: Path, budget: dict[str, Any]) -> None:
372 """Replace an existing budget atomically without changing its mode bits."""
373 original_mode = stat.S_IMODE(path.stat().st_mode)
374 payload = json.dumps(budget, indent=2, sort_keys=True) + "\n"
375 file_descriptor, temporary_name = tempfile.mkstemp(
376 prefix=f".{path.name}.", suffix=".tmp", dir=path.parent
377 )
378 temporary_path = Path(temporary_name)
379 try:
380 with os.fdopen(file_descriptor, "w", encoding="utf-8") as handle:
381 handle.write(payload)
382 handle.flush()
383 os.fsync(handle.fileno())
384 os.chmod(temporary_path, original_mode)
385 os.replace(temporary_path, path)
386 except BaseException:
387 temporary_path.unlink(missing_ok=True)
388 raise
389
390
391 def run_measurement() -> dict[str, Any]:
392 env = os.environ.copy()
393 env["CARGO_NET_OFFLINE"] = "true"
394 proc = subprocess.run(
395 [sys.executable, str(MEASURE_SCRIPT)],
396 cwd=REPO_ROOT,
397 env=env,
398 capture_output=True,
399 text=True,
400 check=False,
401 )
402 sys.stderr.write(proc.stderr)
403 if proc.returncode != 0:
404 sys.stdout.write(proc.stdout)
405 raise RuntimeContractError(
406 f"runtime-contract measurement failed with exit code {proc.returncode}"
407 )
408 try:
409 receipt = json.loads(proc.stdout)
410 except json.JSONDecodeError as error:
411 raise RuntimeContractError(f"measurement emitted invalid JSON: {error}") from error
412 if not isinstance(receipt, dict):
413 raise RuntimeContractError("measurement top level must be an object")
414 validate_receipt(receipt)
415 return receipt
416
417
418 def update_command(receipt_path: Path | None, budget_path: Path) -> str:
419 parts = ["python3", "scripts/check-runtime-contract-budget.py"]
420 if receipt_path is not None:
421 parts.extend(["--receipt", str(receipt_path)])
422 if budget_path != BUDGET_PATH:
423 parts.extend(["--budget", str(budget_path)])
424 parts.append("--update")
425 return shlex.join(parts)
426
427
428 def main(argv: Sequence[str] | None = None) -> int:
429 parser = argparse.ArgumentParser(description=__doc__)
430 parser.add_argument(
431 "--receipt",
432 type=Path,
433 help="check an existing measurement JSON instead of compiling",
434 )
435 parser.add_argument(
436 "--budget",
437 type=Path,
438 default=BUDGET_PATH,
439 help=argparse.SUPPRESS,
440 )
441 parser.add_argument(
442 "--update",
443 action="store_true",
444 help="tighten all ceilings to the current receipt; refuses increases",
445 )
446 args = parser.parse_args(argv)
447
448 try:
449 if args.receipt is not None and args.receipt.resolve() == args.budget.resolve():
450 raise RuntimeContractError(
451 "receipt and budget must resolve to distinct filesystem paths"
452 )
453 budget = load_json(args.budget, "budget")
454 receipt = (
455 load_json(args.receipt, "receipt")
456 if args.receipt is not None
457 else run_measurement()
458 )
459 increases, decreases = compare(receipt, budget)
460 except RuntimeContractError as error:
461 print(f"[runtime-contract-budget] ERROR: {error}", file=sys.stderr)
462 return 2
463
464 if increases:
465 print("[runtime-contract-budget] FAIL: runtime contract grew:", file=sys.stderr)
466 for path, label, current, ceiling in increases:
467 print(
468 f" {label}: {current} > {ceiling} (+{current - ceiling}) [{path}]",
469 file=sys.stderr,
470 )
471 print(
472 "\nReduce the model-facing surface or make any higher ceiling an explicit "
473 "maintainer decision in scripts/runtime-contract-budget.json.",
474 file=sys.stderr,
475 )
476 return 1
477
478 if args.update:
479 try:
480 write_budget_atomic(args.budget, budget_from_receipt(receipt))
481 except OSError as error:
482 print(
483 f"[runtime-contract-budget] ERROR: failed to update budget: {error}",
484 file=sys.stderr,
485 )
486 return 2
487 print(
488 f"[runtime-contract-budget] wrote {args.budget}: "
489 f"{len(decreases)} decreased ceilings locked in "
490 f"({len(METRICS)} total)"
491 )
492 return 0
493
494 if decreases:
495 print(
496 f"[runtime-contract-budget] PASS: {len(METRICS)} ceilings respected; "
497 f"{len(decreases)} can be tightened."
498 )
499 for path, label, current, ceiling in decreases:
500 print(f" {label}: {current} < {ceiling} (-{ceiling - current}) [{path}]")
501 print(f"Tighten with:\n {update_command(args.receipt, args.budget)}")
502 return 0
503
504 print(
505 f"[runtime-contract-budget] PASS: all {len(METRICS)} metrics are exactly at budget."
506 )
507 return 0
508
509
510 if __name__ == "__main__":
511 raise SystemExit(main())
512
512 lines PYTHON