Implement Dispatcher v0.4 local state and controls

This commit is contained in:
2026-09-27 22:45:51 +08:00
parent 7653a4cea8
commit 3ea5f34583
68 changed files with 2375 additions and 460 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p contracts/local/v0.1 contracts/local/v0.3
mkdir -p contracts/local/v0.1 contracts/local/v0.3 contracts/local/v0.4
rm -f contracts/local/v0.1/task-discovery-v0.1-proposal.schema.json
cp docs/contracts/config-read-v0.1.schema.json docs/contracts/command-next-v0.1-proposal.schema.json docs/contracts/call-result-v0.1-proposal.schema.json docs/contracts/local-mock-recording-failure-v0.1.schema.json contracts/local/v0.1/
cp docs/contracts/task-discovery-v0.3-proposal.schema.json contracts/local/v0.3/
cp docs/contracts/task-discovery-v0.4-proposal.schema.json docs/contracts/task-control-v0.4-proposal.schema.json docs/contracts/call-execute-v0.4-proposal.schema.json contracts/local/v0.4/
+58
View File
@@ -384,6 +384,64 @@ for entry in [event_source, *event_artifacts]:
raise SystemExit(f"v0.3 manifest SHA-256 mismatch: {relative}")
print(f"Task discovery v0.3 proposal: positive={event_positive + len(seen_event_errors)}, negative={event_negative}, manifest files={1 + len(event_artifacts)}")
# v0.4 is a new project-local contract, not a mutation of v0.3 or upstream v1.
v4_doc = ROOT / "docs/thirds/v0.4.md"
v4_schema_paths = {
name: ROOT / f"docs/contracts/{name}-v0.4-proposal.schema.json"
for name in ("task-discovery", "task-control", "call-execute")
}
v4_registry = registry.with_resource(event_schema["$id"], Resource.from_contents(event_schema))
v4_examples = {name: sorted(EXAMPLES.glob(f"{name}-*-v0.4.json")) for name in v4_schema_paths}
v4_positive = v4_negative = 0
for name, schema_path in v4_schema_paths.items():
schema = load_json(schema_path)
Draft202012Validator.check_schema(schema)
if schema_versions(schema) != {f"{name}.v0.4-proposal"}:
raise SystemExit(f"unexpected v0.4 schema version: {schema_path}")
embedded = ROOT / "contracts/local/v0.4" / schema_path.name
if embedded.read_bytes() != schema_path.read_bytes():
raise SystemExit(f"embedded v0.4 schema differs from project source: {schema_path.name}")
validator = Draft202012Validator(schema, registry=v4_registry, format_checker=FormatChecker())
positive = negative = 0
for path in v4_examples[name]:
sample = load_json(path)
if "invalid" in path.name:
try:
validator.validate(sample)
except ValidationError:
negative += 1
else:
raise SystemExit(f"invalid v0.4 fixture passed: {path.relative_to(ROOT)}")
else:
validator.validate(sample)
positive += 1
if positive < 3 or negative < 1:
raise SystemExit(f"missing v0.4 positive/negative fixtures for {schema_path.name}")
v4_positive += positive
v4_negative += negative
v4_manifest = load_json(ROOT / "docs/contracts/local-contract-manifest-v0.4.json")
if v4_manifest.get("manifest_version") != "local-contract-manifest.v0.4" or v4_manifest.get("hash_algorithm") != "SHA-256":
raise SystemExit("invalid v0.4 manifest version or hash algorithm")
v4_source = v4_manifest.get("source", {})
v4_artifacts = v4_manifest.get("artifacts", [])
required_v4 = {
*(p.relative_to(ROOT).as_posix() for p in v4_schema_paths.values()),
*(p.relative_to(ROOT).as_posix() for examples in v4_examples.values() for p in examples),
event_schema_path.relative_to(ROOT).as_posix(),
"contracts/upstream/v1/mq.schema.json",
}
v4_paths = [entry.get("path") for entry in v4_artifacts if isinstance(entry, dict)]
if v4_source.get("path") != v4_doc.relative_to(ROOT).as_posix() or len(v4_paths) != len(v4_artifacts) or len(v4_paths) != len(set(v4_paths)) or set(v4_paths) != required_v4:
raise SystemExit("v0.4 manifest source/artifacts mismatch")
for entry in [v4_source, *v4_artifacts]:
relative = Path(entry.get("path", ""))
digest = entry.get("sha256", "")
if relative.is_absolute() or ".." in relative.parts or not re.fullmatch(r"[0-9a-f]{64}", str(digest)):
raise SystemExit(f"invalid v0.4 manifest entry: {entry}")
if not (ROOT / relative).is_file() or hashlib.sha256((ROOT / relative).read_bytes()).hexdigest() != digest:
raise SystemExit(f"v0.4 manifest SHA-256 mismatch: {relative}")
print(f"Task discovery/control/execute v0.4 proposal: positive={v4_positive}, negative={v4_negative}, manifest files={1 + len(v4_artifacts)}")
# The approved Agent→Dispatcher failure fact is Mock-only. Its independent
# manifest must not alter the historical SaaS v0.1 or discovery v0.2 baselines.
mock_doc = ROOT / "docs/contracts/local-mock-recording-failure-v0.1.md"