chore(consistency): sync task status from DB [auto]
Updated by fix-consistency on 2026-10-11: - update .custodian-brief.md for the-custodian
This commit is contained in:
parent
ebaa38bd56
commit
6fe7201fd5
15 changed files with 411 additions and 10 deletions
79
scripts/security_flow_daily.py
Normal file
79
scripts/security_flow_daily.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Daily decision sync and security report, with per-recipient delivery checkpoints.
|
||||
|
||||
Dry-run by default. --apply is the scheduled mode. No credentials are stored.
|
||||
"""
|
||||
import argparse
|
||||
import fcntl
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from security_flow_report import DEFAULT_REPOS
|
||||
|
||||
SCRIPTS = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def notifications(report, state, agent, founder):
|
||||
pending = []
|
||||
ready = {key: report[key] for key in ("READY", "UNBLOCKED")}
|
||||
if ready != state.get("agent"):
|
||||
pending.append(("agent", agent, ready))
|
||||
# ATTENDED is the daily operator reminder; empty lists never message the founder.
|
||||
if report["ATTENDED"]:
|
||||
pending.append(("founder", founder, {"ATTENDED": report["ATTENDED"]}))
|
||||
return pending
|
||||
|
||||
|
||||
def post(hub, recipient, payload):
|
||||
body = {"from_agent": "the-custodian", "to_agent": recipient,
|
||||
"subject": "Daily security flow", "body": json.dumps(payload, sort_keys=True)}
|
||||
request = Request(hub.rstrip("/") + "/messages/", data=json.dumps(body).encode(),
|
||||
headers={"Content-Type": "application/json"}, method="POST")
|
||||
with urlopen(request, timeout=30) as response:
|
||||
return json.load(response)["id"]
|
||||
|
||||
|
||||
def save(path, data):
|
||||
temporary = path.with_suffix(path.suffix + ".tmp")
|
||||
temporary.write_text(json.dumps(data, indent=2) + "\n")
|
||||
temporary.replace(path)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", default=str(Path.home()))
|
||||
parser.add_argument("--repos", default=",".join(["the-custodian", *DEFAULT_REPOS]))
|
||||
parser.add_argument("--hub", default="http://127.0.0.1:8000")
|
||||
parser.add_argument("--agent", default="the-custodian")
|
||||
parser.add_argument("--founder", default="human")
|
||||
parser.add_argument("--state-dir", type=Path, default=Path.home() / ".local/state/custodian-security-flow")
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
args = parser.parse_args()
|
||||
args.state_dir.mkdir(parents=True, exist_ok=True)
|
||||
with (args.state_dir / "run.lock").open("w") as lock:
|
||||
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
command = [sys.executable, str(SCRIPTS / "sync_resolved_decisions.py"),
|
||||
"--root", args.root, "--hub", args.hub]
|
||||
subprocess.run(command + (["--apply"] if args.apply else []), check=True)
|
||||
raw = subprocess.check_output([sys.executable, str(SCRIPTS / "security_flow_report.py"),
|
||||
"--root", args.root, "--repos", args.repos, "--json"], text=True)
|
||||
report = json.loads(raw)
|
||||
state_path = args.state_dir / "delivery.json"
|
||||
state = json.loads(state_path.read_text()) if state_path.exists() else {}
|
||||
save(args.state_dir / "report.json", report)
|
||||
for key, recipient, payload in notifications(report, state, args.agent, args.founder):
|
||||
if not args.apply:
|
||||
print(json.dumps({"dry_run": True, "to_agent": recipient, "report": payload}))
|
||||
continue
|
||||
message_id = post(args.hub, recipient, payload)
|
||||
state[key] = payload
|
||||
state[key + "_message_id"] = message_id
|
||||
save(state_path, state) # A later recipient failure does not replay earlier sends.
|
||||
print(raw)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue