← Files VeraARCHIVED FILE
modules/browser-automation/scripts/development_request.py
17.9 KB · Oct 3, 2026 · 06:30 UTC
#!/usr/bin/env python3
"""Prepare an exact, reviewable development handoff from selected saved evidence.
The model selects and sanitizes facts. Code checks shape, source references,
file integrity and export approval; it neither judges success nor sends data.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import logging
import os
import re
import stat
from pathlib import Path, PurePosixPath
from typing import Any
from zipfile import ZIP_DEFLATED, BadZipFile, ZipFile
from capability_pipeline import canonical_json_bytes, sha256_payload
from discovery_pack import verify_developer_pack
from teaching_checkpoint import read_checkpoint
__all__ = ["prepare_request", "export_request", "verify_archive", "main"]
LOG = logging.getLogger(__name__)
SCHEMA = "browser-development-request/v1"
LIFECYCLE_SCHEMA = "browser-development-request/v2"
MAX_BYTES = 32 * 1024 * 1024
SLUG = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
TEXT_KEYS = {"request_id", "title", "process", "objective", "source_version"}
LIST_KEYS = {"requested_work", "acceptance_checks", "gaps", "known_limits"}
def _text(value: Any) -> bool:
return isinstance(value, str) and bool(value.strip()) and len(value) <= 6000
def _validate(payload: Any) -> None:
lifecycle_keys = (
{"browser_lifecycle"}
if isinstance(payload, dict)
and payload.get("schema_version") == LIFECYCLE_SCHEMA
else set()
)
if (
not isinstance(payload, dict)
or set(payload)
!= TEXT_KEYS
| LIST_KEYS
| {
"schema_version",
"findings",
}
| lifecycle_keys
):
raise ValueError("invalid development request fields")
if payload["schema_version"] not in {SCHEMA, LIFECYCLE_SCHEMA} or not all(
_text(payload[k]) for k in TEXT_KEYS
):
raise ValueError("request identity and purpose are required")
if lifecycle_keys:
lineage = payload["browser_lifecycle"]
if (
not isinstance(lineage, dict)
or not re.fullmatch(
r"process-[a-f0-9]{32}", str(lineage.get("process_id", ""))
)
or not re.fullmatch(
r"attempt-[a-f0-9]{32}", str(lineage.get("attempt_id", ""))
)
):
raise ValueError("persistent process and attempt identity are required")
if not SLUG.fullmatch(payload["request_id"]):
raise ValueError("request_id must be a local slug, not an invented CR number")
for key in LIST_KEYS:
values = payload[key]
if (
not isinstance(values, list)
or len(values) > 100
or not all(_text(v) for v in values)
):
raise ValueError("request lists must contain bounded text")
if not payload["requested_work"] or not payload["acceptance_checks"]:
raise ValueError("requested work and concrete acceptance checks are required")
findings = payload["findings"]
if not isinstance(findings, list) or len(findings) > 100:
raise ValueError("findings must be a bounded array")
for finding in findings:
if (
not isinstance(finding, dict)
or set(finding) != {"summary", "basis", "step_ids"}
or not _text(finding["summary"])
):
raise ValueError("invalid finding")
if finding["basis"] not in {"observed", "operator_report", "unknown"}:
raise ValueError("findings must distinguish observation from reports")
refs = finding["step_ids"]
if not isinstance(refs, list) or not all(_text(r) for r in refs):
raise ValueError("invalid step references")
if finding["basis"] == "observed" and not refs:
raise ValueError(
"observed findings require saved checkpoint step references"
)
def _read_file(path: Path) -> bytes:
if any(p.is_symlink() for p in (path, *path.parents)) or not path.is_file():
raise ValueError("only regular files without symlink ancestry are allowed")
if path.stat().st_size > MAX_BYTES:
raise ValueError("file exceeds handoff size limit")
return path.read_bytes()
def _safe_name(name: str) -> bool:
path = PurePosixPath(name)
return (
name not in {"", "."}
and "\\" not in name
and ":" not in name
and not path.is_absolute()
and ".." not in path.parts
and path.as_posix() == name
)
def _validate_manifest(manifest: Any) -> None:
if not isinstance(manifest, dict) or set(manifest) != {
"schema_version",
"request_id",
"files",
}:
raise ValueError("invalid review manifest")
files = manifest["files"]
if (
manifest["schema_version"] != SCHEMA
or not isinstance(manifest["request_id"], str)
or not SLUG.fullmatch(manifest["request_id"])
):
raise ValueError("invalid review identity")
if not isinstance(files, dict) or not {
"request.json",
"sources.json",
"RICHIESTA.md",
} <= set(files):
raise ValueError("required review files are missing")
if {"review-manifest.json", "transfer-approval.json"} & set(files):
raise ValueError("reserved review file names")
if any(
not _safe_name(n)
or not isinstance(d, str)
or not re.fullmatch(r"[a-f0-9]{64}", d)
for n, d in files.items()
):
raise ValueError("invalid review file hash or path")
def _write(path: Path, content: bytes) -> None:
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "wb") as stream:
stream.write(content)
def prepare_request(
payload: dict[str, Any],
directory: Path,
*,
checkpoint: Path | None = None,
developer_pack: Path | None = None,
cr_body: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Prepare local files only. No raw checkpoint or business review is copied."""
_validate(payload)
if len(canonical_json_bytes(payload)) > 48 * 1024:
raise ValueError("structured request exceeds the CR text limit")
sources: dict[str, Any] = {"checkpoint": None, "developer_pack_attached": False}
steps: dict[str, Any] = {}
if checkpoint is not None:
record = read_checkpoint(checkpoint)
sources["checkpoint"] = {
"revision": record["revision"],
"sha256": record["sha256"],
}
steps = {s["id"]: s for s in record["payload"]["steps"]}
for finding in payload["findings"]:
if any(ref not in steps for ref in finding["step_ids"]):
raise ValueError("finding references an unavailable checkpoint step")
if finding["basis"] == "observed" and any(
steps[r]["evidence_basis"] != "observed" for r in finding["step_ids"]
):
raise ValueError("operator reports cannot be promoted to observed evidence")
# Export only a bounded evidence projection for referenced steps; the model
# supplies sanitized meanings in findings, so original private text stays out.
selected = sorted({r for f in payload["findings"] for r in f["step_ids"]})
sources["step_evidence"] = [
{"id": r, "basis": steps[r]["evidence_basis"], "capture": steps[r]["capture"]}
for r in selected
]
files = {
"request.json": canonical_json_bytes(payload),
"sources.json": canonical_json_bytes(sources),
}
if cr_body is not None:
if payload["schema_version"] != LIFECYCLE_SCHEMA:
raise ValueError("CR body requires persisted lifecycle identity")
if len(canonical_json_bytes(cr_body)) > 48 * 1024:
raise ValueError("CR body exceeds submission size limit")
files["cr-request.json"] = canonical_json_bytes(cr_body)
if developer_pack is not None:
errors = verify_developer_pack(developer_pack)
if errors:
raise ValueError("developer pack integrity or lineage validation failed")
evidence = json.loads(_read_file(developer_pack / "discovery-evidence.json"))
if (
evidence["review"]["operator_reviewed"] is not True
or evidence["review"]["approved_for_developer_transfer"] is not True
):
raise ValueError("developer pack needs actual operator transfer approval")
lock = json.loads(_read_file(developer_pack / "developer-pack.lock.json"))
for name in [*lock["files"], "developer-pack.lock.json"]:
if not _safe_name(name):
raise ValueError("unsafe developer pack path")
content = _read_file(developer_pack / name)
if (
name in lock["files"]
and hashlib.sha256(content).hexdigest() != lock["files"][name]
):
raise ValueError("developer pack changed while preparing")
files["developer-pack/" + name] = content
sources["developer_pack_attached"] = True
files["sources.json"] = canonical_json_bytes(sources)
labels = {
"observed": "Osservato",
"operator_report": "Riferito dall’operatore",
"unknown": "Da verificare",
}
lines = [
f"# {payload['title']}",
"",
f"Processo: {payload['process']}",
f"Versione osservata: {payload['source_version']}",
"",
"## Obiettivo",
payload["objective"],
"",
"## Risultati e loro evidenza",
]
lines += [
f"- {labels[f['basis']]}: {f['summary']}" for f in payload["findings"]
] or ["- Nessun risultato documentato."]
if payload["schema_version"] == LIFECYCLE_SCHEMA:
lineage = payload["browser_lifecycle"]
lines += [
"",
"## Processo e tentativo persistenti",
f"Processo: {lineage['process_id']}",
f"Tentativo: {lineage['attempt_id']}",
"Le prove tecniche selezionate sono incluse nel corpo CR revisionabile; i file ZIP non vengono caricati dal servizio CR.",
]
for key, label in [
("requested_work", "Lavoro richiesto"),
("acceptance_checks", "Come verificheremo il risultato"),
("gaps", "Informazioni mancanti"),
("known_limits", "Limiti noti"),
]:
lines += ["", "## " + label] + (
[f"- {v}" for v in payload[key]] or ["- Nessuno indicato."]
)
lines += [
"",
"## Materiale per lo sviluppo",
(
"Developer pack revisionato allegato."
if developer_pack
else "Developer pack tecnico non disponibile: questa richiesta conserva il materiale utile senza inventare una capability o una validazione."
),
"",
"Questa è una richiesta locale, non un CR già trasmesso. Non autorizza esecuzione o pubblicazione. Il significato e la riservatezza dei contenuti devono essere verificati dal modello e dall’operatore; gli hash provano soltanto integrità.",
"",
"## File da rivedere prima dell’esportazione",
]
lines += ["- " + name for name in sorted(files)]
files["RICHIESTA.md"] = ("\n".join(lines) + "\n").encode()
if sum(map(len, files.values())) > MAX_BYTES:
raise ValueError("handoff exceeds size limit")
manifest = {
"schema_version": SCHEMA,
"request_id": payload["request_id"],
"files": {
name: hashlib.sha256(data).hexdigest()
for name, data in sorted(files.items())
},
}
if any(p.is_symlink() for p in (directory, *directory.parents)):
raise ValueError("output directory must not use symlinks")
directory.mkdir(mode=0o700)
for name, data in files.items():
_write(directory / name, data)
_write(directory / "review-manifest.json", canonical_json_bytes(manifest))
return {
"review_path": str(directory / "RICHIESTA.md"),
"review_sha256": sha256_payload(manifest),
"status": "awaiting_operator_review",
"archive_created": False,
}
def export_request(
directory: Path, output: Path, *, expected_sha256: str, approval_id: str
) -> Path:
"""Export only exact reviewed files after explicit approval of their content."""
if not _text(approval_id):
raise ValueError("explicit operator approval reference required")
manifest_bytes = _read_file(directory / "review-manifest.json")
manifest = json.loads(manifest_bytes)
if sha256_payload(manifest) != expected_sha256:
raise ValueError("review changed; review the current files again")
_validate_manifest(manifest)
files = {}
for name, digest in manifest["files"].items():
if not _safe_name(name):
raise ValueError("unsafe review file path")
data = _read_file(directory / name)
if hashlib.sha256(data).hexdigest() != digest:
raise ValueError("review file changed; prepare and review a new request")
files[name] = data
actual = {
p.relative_to(directory).as_posix()
for p in directory.rglob("*")
if p.is_file() or p.is_symlink()
}
if actual != set(files) | {"review-manifest.json"}:
raise ValueError("review contains unlisted files")
if sum(map(len, files.values())) > MAX_BYTES:
raise ValueError("handoff exceeds size limit")
request = json.loads(files["request.json"])
_validate(request)
if request["request_id"] != manifest["request_id"]:
raise ValueError("request identity mismatch")
files["review-manifest.json"] = manifest_bytes
files["transfer-approval.json"] = canonical_json_bytes(
{
"review_sha256": expected_sha256,
"approval_id": approval_id,
"scope": "export_only_not_sent",
}
)
if sum(map(len, files.values())) > MAX_BYTES:
raise ValueError("archive exceeds size limit")
if output.suffix.lower() != ".zip" or any(
p.is_symlink() for p in (output, *output.parents)
):
raise ValueError("output must be a new ZIP without symlinks")
fd = os.open(output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with (
os.fdopen(fd, "wb") as stream,
ZipFile(stream, "w", compression=ZIP_DEFLATED) as archive,
):
for name, data in files.items():
archive.writestr(name, data)
verify_archive(output)
return output
def verify_archive(path: Path) -> dict[str, Any]:
"""Check exact names, bounded sizes and bytes without extracting any files."""
_read_file(path)
with ZipFile(path) as archive:
entries = archive.infolist()
names = [e.filename for e in entries]
if (
len(names) != len(set(names))
or any(not _safe_name(n) for n in names)
or sum(e.file_size for e in entries) > MAX_BYTES
or any(stat.S_ISLNK(e.external_attr >> 16) for e in entries)
):
raise ValueError("unsafe archive entries or size")
manifest = json.loads(archive.read("review-manifest.json"))
_validate_manifest(manifest)
approval = json.loads(archive.read("transfer-approval.json"))
if not isinstance(approval, dict) or set(approval) != {
"review_sha256",
"approval_id",
"scope",
}:
raise ValueError("invalid archive approval")
if (
approval["review_sha256"] != sha256_payload(manifest)
or not _text(approval["approval_id"])
or approval["scope"] != "export_only_not_sent"
):
raise ValueError("archive approval mismatch")
if set(names) != set(manifest["files"]) | {
"review-manifest.json",
"transfer-approval.json",
}:
raise ValueError("archive contains unlisted or missing files")
for name, digest in manifest["files"].items():
if hashlib.sha256(archive.read(name)).hexdigest() != digest:
raise ValueError("archive file hash mismatch")
request = json.loads(archive.read("request.json"))
_validate(request)
if request["request_id"] != manifest["request_id"]:
raise ValueError("request identity mismatch")
return {
"request_id": manifest["request_id"],
"integrity_verified": True,
"sent": False,
"cr_id": None,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
subs = parser.add_subparsers(dest="command", required=True)
prepare = subs.add_parser("prepare")
prepare.add_argument("--input", type=Path, required=True)
prepare.add_argument("--output", type=Path, required=True)
prepare.add_argument("--checkpoint", type=Path)
prepare.add_argument("--developer-pack", type=Path)
export = subs.add_parser("export")
export.add_argument("directory", type=Path)
export.add_argument("--output", type=Path, required=True)
export.add_argument("--review-sha256", required=True)
export.add_argument("--approval-id", required=True)
verify = subs.add_parser("verify")
verify.add_argument("archive", type=Path)
args = parser.parse_args(argv)
try:
if args.command == "prepare":
result: Any = prepare_request(
json.loads(_read_file(args.input)),
args.output,
checkpoint=args.checkpoint,
developer_pack=args.developer_pack,
)
elif args.command == "export":
result = str(
export_request(
args.directory,
args.output,
expected_sha256=args.review_sha256,
approval_id=args.approval_id,
)
)
else:
result = verify_archive(args.archive)
LOG.info("%s", json.dumps(result, ensure_ascii=False))
except (ValueError, OSError, KeyError, BadZipFile) as exc:
LOG.error("%s", exc)
return 1
return 0
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format="%(message)s")
raise SystemExit(main())
SHA-256: c7dbbe2e707b9b0892e3ad6fcb2c514461c296776268e79e83d5095b1f6dc418