checkpoint: complete recoverable V2 pre-separation baseline
Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
This commit is contained in:
1 parent
7330ac853b
commit
694c4317a3
627 files changed
+130467
-885
No files matched your search
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a deterministic HOLD booking-desk-event `.skill` archive from repository source."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ARCHIVE_ROOT = "booking-desk-event"
|
||||
FIXED_TIMESTAMP = (2026, 8, 19, 0, 0, 0)
|
||||
FIXED_TIMESTAMP_UTC = "2026-08-19T00:00:00Z"
|
||||
RELEASE = "booking-agent-skill-v2-rc4"
|
||||
PUBLICATION_STATUS = "HOLD"
|
||||
DISTRIBUTION = "INTERNAL_ONLY"
|
||||
ALLOWED_TOP_LEVEL = {"SKILL.md", "agents", "manifest.json", "schemas", "references", "fixtures"}
|
||||
REQUIRED_FILES = {
|
||||
"SKILL.md",
|
||||
"agents/openai.yaml",
|
||||
"manifest.json",
|
||||
"schemas/booking-business-agent-compact-input-v1.schema.json",
|
||||
"schemas/booking-business-agent-compact-decision-v1.schema.json",
|
||||
"fixtures/scenario-matrix.json",
|
||||
}
|
||||
|
||||
|
||||
def fail(message: str) -> None:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def sha256_bytes(value: bytes) -> str:
|
||||
return hashlib.sha256(value).hexdigest()
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
return sha256_bytes(path.read_bytes())
|
||||
|
||||
|
||||
def source_files(source: Path) -> list[Path]:
|
||||
if not source.is_dir():
|
||||
fail(f"Skill source directory is missing: {source}")
|
||||
main_prompt = source / "prompts/main-prompt.md"
|
||||
if not main_prompt.is_file():
|
||||
fail(f"Independent Main Prompt is missing: {main_prompt}")
|
||||
files = sorted(
|
||||
(
|
||||
path for path in source.rglob("*")
|
||||
if path.is_file() and path.relative_to(source).parts[0] != "prompts"
|
||||
),
|
||||
key=lambda path: path.relative_to(source).as_posix(),
|
||||
)
|
||||
relative = {path.relative_to(source).as_posix() for path in files}
|
||||
missing = REQUIRED_FILES - relative
|
||||
if missing:
|
||||
fail(f"Skill source is missing required members: {', '.join(sorted(missing))}")
|
||||
forbidden = [
|
||||
value for value in relative
|
||||
if value.split("/", 1)[0] not in ALLOWED_TOP_LEVEL
|
||||
]
|
||||
if forbidden:
|
||||
fail(f"Skill source contains a forbidden top-level member: {forbidden[0]}")
|
||||
if any(path.name in {".DS_Store"} or "__pycache__" in path.parts for path in files):
|
||||
fail("Skill source contains an OS/cache member")
|
||||
return files
|
||||
|
||||
|
||||
def archive_member(source: Path, file_path: Path) -> str:
|
||||
return f"{ARCHIVE_ROOT}/{file_path.relative_to(source).as_posix()}"
|
||||
|
||||
|
||||
def write_archive(source: Path, output: Path) -> list[str]:
|
||||
files = source_files(source)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
|
||||
for file_path in files:
|
||||
info = zipfile.ZipInfo(archive_member(source, file_path), FIXED_TIMESTAMP)
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
archive.writestr(info, file_path.read_bytes(), compress_type=zipfile.ZIP_DEFLATED, compresslevel=9)
|
||||
return [archive_member(source, path) for path in files]
|
||||
|
||||
|
||||
def validate_archive(source: Path, archive_path: Path) -> list[str]:
|
||||
expected_files = source_files(source)
|
||||
expected_members = [archive_member(source, path) for path in expected_files]
|
||||
if not archive_path.is_file():
|
||||
fail(f"Archive is missing: {archive_path}")
|
||||
with zipfile.ZipFile(archive_path) as archive:
|
||||
if archive.namelist() != expected_members:
|
||||
fail("Archive member list does not exactly match the allowed source")
|
||||
if any("/prompts/" in member or member.endswith("/main-prompt.md") for member in archive.namelist()):
|
||||
fail("Main Prompt was packaged into the Skill")
|
||||
for source_file, member in zip(expected_files, expected_members):
|
||||
if archive.read(member) != source_file.read_bytes():
|
||||
fail(f"Archive content differs from source: {member}")
|
||||
return expected_members
|
||||
|
||||
|
||||
def build_manifest(source: Path, archive: Path, members: list[str]) -> dict[str, object]:
|
||||
files = source_files(source)
|
||||
member_hashes = {
|
||||
archive_member(source, file_path): sha256_bytes(file_path.read_bytes())
|
||||
for file_path in files
|
||||
}
|
||||
scenario_matrix = json.loads((source / "fixtures/scenario-matrix.json").read_text(encoding="utf-8"))
|
||||
source_manifest = json.loads((source / "manifest.json").read_text(encoding="utf-8"))
|
||||
if source_manifest.get("publication_status") != PUBLICATION_STATUS:
|
||||
fail("Editable manifest publication_status must be HOLD")
|
||||
if source_manifest.get("distribution") != DISTRIBUTION:
|
||||
fail("Editable manifest distribution must be INTERNAL_ONLY")
|
||||
if source_manifest.get("main_prompt_packaged") is not False:
|
||||
fail("Editable manifest must declare main_prompt_packaged=false")
|
||||
return {
|
||||
"manifest_schema_version": 1,
|
||||
"artifact": archive.name,
|
||||
"release": RELEASE,
|
||||
"skill_release_version": "Booking Agent Skill V2",
|
||||
"business_version": "booking-business-agent-v1.0",
|
||||
"input_contract": "booking-business-agent-compact-input-v1",
|
||||
"output_contract": "booking-business-agent-compact-decision-v1",
|
||||
"instruction_language": "zh-CN",
|
||||
"publication_status": PUBLICATION_STATUS,
|
||||
"distribution": DISTRIBUTION,
|
||||
"publication_hold_reason": (
|
||||
"Booking Agent Skill V2 still requires publication to the bound SuperAgent Profile and one real EML revalidation."
|
||||
),
|
||||
"main_prompt_packaged": False,
|
||||
"main_prompt_sha256": sha256(source / "prompts/main-prompt.md"),
|
||||
"scenario_count": scenario_matrix.get("scenario_count"),
|
||||
"sha256": sha256(archive),
|
||||
"member_count": len(members),
|
||||
"members": members,
|
||||
"member_sha256": member_hashes,
|
||||
"archive_timestamp_utc": FIXED_TIMESTAMP_UTC,
|
||||
}
|
||||
|
||||
|
||||
def render_manifest(manifest: dict[str, object]) -> bytes:
|
||||
return (json.dumps(manifest, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
|
||||
|
||||
|
||||
def write_or_check_manifest(path: Path, manifest: dict[str, object], check: bool) -> None:
|
||||
expected = render_manifest(manifest)
|
||||
if check:
|
||||
if not path.is_file() or path.read_bytes() != expected:
|
||||
fail(f"Package manifest is stale or missing: {path}")
|
||||
return
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(expected)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--source", required=True, type=Path)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
parser.add_argument("--manifest", required=True, type=Path)
|
||||
parser.add_argument("--check", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
source = args.source.resolve()
|
||||
output = args.output.resolve()
|
||||
if args.check:
|
||||
with tempfile.TemporaryDirectory(prefix="booking-desk-event-package-") as directory:
|
||||
expected = Path(directory) / output.name
|
||||
write_archive(source, expected)
|
||||
if not output.is_file() or expected.read_bytes() != output.read_bytes():
|
||||
fail("Archive is not reproducible from the editable source")
|
||||
else:
|
||||
write_archive(source, output)
|
||||
members = validate_archive(source, output)
|
||||
write_or_check_manifest(args.manifest.resolve(), build_manifest(source, output, members), args.check)
|
||||
print(f"Validated Skill archive: {output} ({len(members)} files, sha256={sha256(output)})")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except (OSError, ValueError, json.JSONDecodeError, zipfile.BadZipFile) as error:
|
||||
print(f"Packaging failed: {error}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
Reference in new issue
Block a user