feat(review): add multi-owner contracts and receipts
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
52aefe39e4
commit
598f6418e7
13 changed files with 1401 additions and 9 deletions
|
|
@ -18,6 +18,7 @@ import re
|
|||
import subprocess
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -571,6 +572,73 @@ def cmd_status(_args: argparse.Namespace) -> None:
|
|||
print(f" [{deadline}] {d['title']}")
|
||||
|
||||
|
||||
def _load_json_file(path: str) -> dict:
|
||||
target = Path(path)
|
||||
try:
|
||||
value = json.loads(target.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
print(f"ERROR: invalid JSON file {target}: {exc}")
|
||||
sys.exit(2)
|
||||
if not isinstance(value, dict):
|
||||
print(f"ERROR: {target} must contain a JSON object")
|
||||
sys.exit(2)
|
||||
return value
|
||||
|
||||
|
||||
def _git_source(path: str, repo_slug: str | None = None) -> tuple[Path, dict]:
|
||||
target = Path(path).resolve()
|
||||
try:
|
||||
root = Path(subprocess.check_output(
|
||||
["git", "-C", str(target.parent), "rev-parse", "--show-toplevel"],
|
||||
text=True,
|
||||
).strip())
|
||||
revision = subprocess.check_output(
|
||||
["git", "-C", str(root), "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
relative = target.relative_to(root).as_posix()
|
||||
except (subprocess.CalledProcessError, ValueError) as exc:
|
||||
print(f"ERROR: {target} must be inside a Git repository: {exc}")
|
||||
sys.exit(2)
|
||||
return target, {"repo": repo_slug or root.name, "path": relative, "revision": revision}
|
||||
|
||||
|
||||
def cmd_review_project(args: argparse.Namespace) -> None:
|
||||
"""Project a file-authoritative contract wrapper through the direct API."""
|
||||
target, source = _git_source(args.file, args.source_repo)
|
||||
contract = _load_json_file(str(target))
|
||||
payload = contract if "contract" in contract and "source" in contract else {
|
||||
"contract": contract,
|
||||
"source": source,
|
||||
"decision_id": args.decision_id,
|
||||
"workplan_id": args.workplan_id,
|
||||
"task_id": args.task_id,
|
||||
"required_for_decision": args.required_for_decision,
|
||||
}
|
||||
print(json.dumps(
|
||||
_api_post("/review-contracts/projections", payload),
|
||||
indent=2,
|
||||
))
|
||||
|
||||
|
||||
def cmd_review_submit(args: argparse.Namespace) -> None:
|
||||
"""Submit a file-authoritative receipt against an active contract."""
|
||||
key = urllib.parse.quote(args.contract_key, safe="")
|
||||
target, source = _git_source(args.file, args.source_repo)
|
||||
receipt = _load_json_file(str(target))
|
||||
if "source" not in receipt:
|
||||
receipt["source"] = source
|
||||
print(json.dumps(
|
||||
_api_post(f"/review-contracts/{key}/receipts", receipt),
|
||||
indent=2,
|
||||
))
|
||||
|
||||
|
||||
def cmd_review_status(args: argparse.Namespace) -> None:
|
||||
"""Print the derived owner and gate matrix; never an execution authorization."""
|
||||
key = urllib.parse.quote(args.contract_key, safe="")
|
||||
print(json.dumps(_api_get(f"/review-contracts/{key}/aggregate"), indent=2))
|
||||
|
||||
|
||||
|
||||
def _outbox_store(args):
|
||||
from api.edge.outbox import OutboxStore, default_outbox_path
|
||||
|
|
@ -816,6 +884,26 @@ def main() -> None:
|
|||
# status
|
||||
sub.add_parser("status", help="Show State Hub health and summary totals")
|
||||
|
||||
# review — file-backed multi-owner review projection
|
||||
review = sub.add_parser("review", help="Project and inspect multi-owner review evidence")
|
||||
review_sub = review.add_subparsers(dest="review_command", required=True)
|
||||
review_project = review_sub.add_parser("project", help="Project an authoritative contract wrapper")
|
||||
review_project.add_argument("file")
|
||||
review_project.add_argument("--source-repo", default=None)
|
||||
review_project.add_argument("--decision-id", default=None)
|
||||
review_project.add_argument("--workplan-id", default=None)
|
||||
review_project.add_argument("--task-id", default=None)
|
||||
review_project.add_argument("--required-for-decision", action="store_true")
|
||||
review_project.set_defaults(func=cmd_review_project)
|
||||
review_submit = review_sub.add_parser("submit", help="Submit an authoritative receipt file")
|
||||
review_submit.add_argument("contract_key")
|
||||
review_submit.add_argument("file")
|
||||
review_submit.add_argument("--source-repo", default=None)
|
||||
review_submit.set_defaults(func=cmd_review_submit)
|
||||
review_status = review_sub.add_parser("status", help="Print owner and gate status")
|
||||
review_status.add_argument("contract_key")
|
||||
review_status.set_defaults(func=cmd_review_status)
|
||||
|
||||
# dev up — files-first local hub (CUST-WP-0054-T07)
|
||||
dev = sub.add_parser("dev", help="Local dev-hub commands")
|
||||
dev_sub = dev.add_subparsers(dest="dev_command", required=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue