| |
| """Preview a loop contract as a readable operating plan.""" |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import json |
| from pathlib import Path |
| from typing import Any |
|
|
| from check_loop_contract_examples import SCHEMA_PATH, validate |
|
|
|
|
| def bullet_list(items: list[str]) -> str: |
| return "\n".join(f" - {item}" for item in items) |
|
|
|
|
| def render_contract(contract: dict[str, Any]) -> str: |
| trigger = contract["trigger"] |
| intake = contract["intake"] |
| workspace = contract["workspace"] |
| context = contract["context"] |
| verification = contract["verification"] |
| state = contract["state"] |
| budget = contract["budget"] |
| escalation = contract["escalation"] |
| exit_rules = contract["exit"] |
|
|
| agents = "\n".join( |
| f" - {agent['role']}: {agent['responsibility']}" for agent in contract["agents"] |
| ) |
|
|
| return f"""# {contract['name']} |
| |
| Objective: |
| {contract['objective']} |
| |
| Trigger: |
| Type: {trigger['type']} |
| Cadence or event: {trigger['cadence_or_event']} |
| |
| Discover / Intake: |
| {bullet_list(intake['sources'])} |
| Selection rule: {intake['selection_rule']} |
| |
| Workspace: |
| Isolation: {workspace['isolation']} |
| Allowed actions: |
| {bullet_list(workspace['allowed_actions'])} |
| Disallowed actions: |
| {bullet_list(workspace['disallowed_actions'])} |
| |
| Context: |
| Required files: |
| {bullet_list(context['required_files'])} |
| Runtime sources: |
| {bullet_list(context['runtime_sources'])} |
| |
| Agents: |
| {agents} |
| |
| Verification: |
| Gates: |
| {bullet_list(verification['gates'])} |
| Receipts: |
| {bullet_list(verification['receipts'])} |
| |
| State: |
| Artifacts: |
| {bullet_list(state['artifacts'])} |
| Update rule: {state['update_rule']} |
| |
| Budget: |
| Max retries: {budget['max_retries']} |
| Max runtime minutes: {budget['max_runtime_minutes']} |
| |
| Escalation: |
| Conditions: |
| {bullet_list(escalation['conditions'])} |
| Destination: {escalation['destination']} |
| |
| Exit: |
| Success: {exit_rules['success']} |
| Stop without success: {exit_rules['stop_without_success']} |
| """ |
|
|
|
|
| def main() -> int: |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument("contract", type=Path, help="Path to a loop contract JSON file.") |
| args = parser.parse_args() |
|
|
| schema = json.loads(SCHEMA_PATH.read_text(encoding="utf-8")) |
| contract = json.loads(args.contract.read_text(encoding="utf-8")) |
| errors = validate(contract, schema, str(args.contract)) |
| if errors: |
| for error in errors: |
| print(error) |
| return 1 |
|
|
| print(render_contract(contract)) |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|