Some checks are pending
CI / lint-and-test (push) Waiting to run
- Add reusable Teleo/Leo skills for VPS, GCP, Telegram, KB changes, provenance, and handoff onboarding. - Add guarded KB proposal tooling for approved rich proposals, apply packets, and rollback proof. - Add Cory-style open-ended benchmark prompts and tests alongside the precise regression canaries. `.agents/skills/leo-telegram-canary-ops/SKILL.md` `.agents/skills/teleo-gcp-parity-ops/SKILL.md` `.agents/skills/teleo-infra-provenance/SKILL.md` `.agents/skills/teleo-kb-db-change-workflow/SKILL.md` `.agents/skills/teleo-leo-onboarding/SKILL.md` `.agents/skills/teleo-proof-handoff/SKILL.md` `.agents/skills/teleo-vps-runtime-ops/SKILL.md` `.agents/skills/working-leo-cory-outcomes/SKILL.md` `docs/reports/leo-working-state-20260709/claim-source-contract-preview-current.md` `docs/reports/leo-working-state-20260709/cory-expected-working-leo-outcomes-20260709.md` `docs/reports/leo-working-state-20260709/current-truth-index.md` `docs/reports/leo-working-state-20260709/fable-leo-teleo-onboarding.md` `docs/reports/leo-working-state-20260709/gcp-db-parity-current-blocker-20260709.json` `docs/reports/leo-working-state-20260709/helmer-7powers-creation-plan.json` `docs/reports/leo-working-state-20260709/kb-apply-canary-execute-current.md` `docs/reports/leo-working-state-20260709/kb-apply-canary-plan-current.md` `docs/reports/leo-working-state-20260709/leo-db-state-13-skill-pack.svg` `docs/reports/leo-working-state-20260709/operator-surface-map.md` `docs/reports/leo-working-state-20260709/production-apply-packet-current.md` `docs/reports/leo-working-state-20260709/rich-proposal-creation-plan-mapped-current.md` `docs/reports/leo-working-state-20260709/skill-pack-manifest.json` `docs/reports/leo-working-state-20260709/skill-pack-readme.md` `docs/reports/leo-working-state-20260709/telegram-gateway-handler-canary-current.json` `docs/reports/leo-working-state-20260709/telegram-gateway-handler-canary-current.md` `docs/reports/leo-working-state-20260709/telegram-live-canary-current.json` `docs/reports/leo-working-state-20260709/telegram-live-canary-current.md` `docs/reports/leo-working-state-20260709/telegram-live-db-write-canary-20260709.json` `docs/reports/leo-working-state-20260709/telegram-live-db-write-canary-20260709.md` `docs/reports/leo-working-state-20260709/working-leo-current-state-20260709.md` `docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md` `docs/reports/leo-working-state-20260709/working-leo-execution-plan-current.md` `docs/reports/leo-working-state-20260709/working-leo-open-ended-benchmark-spec.json` `scripts/approve_proposal.py` `scripts/kb_claim_source_contract_preview.py` `scripts/kb_rich_proposal_apply_packet.py` `scripts/kb_rich_proposal_creation_plan.py` `scripts/working_leo_open_ended_benchmark.py` `tests/test_approve_proposal.py` `tests/test_kb_claim_source_contract_preview.py` `tests/test_kb_rich_proposal_apply_packet.py` `tests/test_kb_rich_proposal_creation_plan.py` `tests/test_working_leo_open_ended_benchmark.py`
160 lines
6 KiB
Python
160 lines
6 KiB
Python
#!/usr/bin/env python3
|
|
"""Approve a strict kb_stage proposal for guarded canonical apply.
|
|
|
|
Stage 1 of the KB apply pipeline: REVIEW -> approve -> apply -> render.
|
|
|
|
This tool does not create canonical rows. It only moves exactly one strict,
|
|
pending, applyable proposal from ``pending_review`` to ``approved`` after a
|
|
human/operator supplies a reviewer handle and verbatim review note. The apply
|
|
step remains separate and is performed by ``scripts/apply_proposal.py``.
|
|
|
|
The guardrails intentionally mirror the apply worker:
|
|
|
|
* proposal must be ``pending_review``;
|
|
* proposal_type must be one of the worker-supported applyable types;
|
|
* payload must contain a strict ``apply_payload`` contract;
|
|
* the strict payload must build valid apply SQL before approval is recorded;
|
|
* the UPDATE must affect exactly one row.
|
|
|
|
``--dry-run`` validates the proposal and prints the SQL without writing.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(HERE))
|
|
|
|
import apply_proposal as ap # noqa: E402
|
|
|
|
|
|
def load_review_note(args: argparse.Namespace) -> str:
|
|
if args.review_note and args.review_note_file:
|
|
raise SystemExit("use either --review-note or --review-note-file, not both")
|
|
note = Path(args.review_note_file).read_text(encoding="utf-8") if args.review_note_file else args.review_note or ""
|
|
note = note.strip()
|
|
if not note:
|
|
raise SystemExit("review note is required and must be non-empty")
|
|
return note
|
|
|
|
|
|
def assert_approvable(proposal: dict[str, Any], reviewed_by: str, review_note: str) -> None:
|
|
if not reviewed_by.strip():
|
|
raise SystemExit("--reviewed-by is required and must be non-empty")
|
|
if not review_note.strip():
|
|
raise SystemExit("--review-note is required and must be non-empty")
|
|
|
|
status = proposal.get("status")
|
|
proposal_id = proposal.get("id")
|
|
if status == "approved":
|
|
raise SystemExit(f"proposal {proposal_id} is already approved")
|
|
if status == "applied":
|
|
raise SystemExit(f"proposal {proposal_id} is already applied")
|
|
if status != "pending_review":
|
|
raise SystemExit(
|
|
f"proposal {proposal_id} has status {status!r}; only 'pending_review' proposals can be approved"
|
|
)
|
|
|
|
ptype = proposal.get("proposal_type")
|
|
if ptype not in ap.APPLYABLE_TYPES:
|
|
raise SystemExit(
|
|
f"proposal {proposal_id} has proposal_type {ptype!r}; only {ap.APPLYABLE_TYPES!r} are applyable"
|
|
)
|
|
|
|
payload = proposal.get("payload") or {}
|
|
if not isinstance(payload, dict) or payload.get("apply_payload") is None:
|
|
raise SystemExit(
|
|
f"proposal {proposal_id} has no payload.apply_payload; run/repair normalization before approval"
|
|
)
|
|
|
|
# Validate the strict contract before recording approval. This is pure SQL
|
|
# building: it does not execute canonical writes.
|
|
ap.build_apply_sql(proposal, ap.SERVICE_AGENT_HANDLE)
|
|
|
|
|
|
def build_approve_sql(proposal_id: str, reviewed_by: str, review_note: str) -> str:
|
|
allowed_types_sql = ", ".join(ap.sql_literal(t) for t in ap.APPLYABLE_TYPES)
|
|
return f"""
|
|
begin;
|
|
with reviewed as (
|
|
update kb_stage.kb_proposals
|
|
set status = 'approved',
|
|
reviewed_by_handle = {ap.sql_literal(reviewed_by)},
|
|
reviewed_at = now(),
|
|
review_note = {ap.sql_literal(review_note)},
|
|
updated_at = now()
|
|
where id = {ap.sql_literal(proposal_id)}::uuid
|
|
and status = 'pending_review'
|
|
and proposal_type in ({allowed_types_sql})
|
|
and payload ? 'apply_payload'
|
|
returning id, proposal_type, status, reviewed_by_handle, reviewed_at, review_note
|
|
)
|
|
select coalesce(
|
|
jsonb_agg(jsonb_build_object(
|
|
'id', id::text,
|
|
'proposal_type', proposal_type,
|
|
'status', status,
|
|
'reviewed_by_handle', reviewed_by_handle,
|
|
'reviewed_at', reviewed_at::text,
|
|
'review_note', review_note
|
|
)),
|
|
'[]'::jsonb
|
|
)::text
|
|
from reviewed;
|
|
commit;
|
|
""".lstrip()
|
|
|
|
|
|
def parse_reviewed_rows(output: str, proposal_id: str) -> list[dict[str, Any]]:
|
|
rows = json.loads(output.strip() or "[]")
|
|
if not isinstance(rows, list):
|
|
raise SystemExit("approval update returned non-list JSON")
|
|
if len(rows) != 1:
|
|
raise SystemExit(
|
|
f"approval update affected {len(rows)} rows for proposal {proposal_id}; expected exactly 1"
|
|
)
|
|
return rows
|
|
|
|
|
|
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
parser.add_argument("proposal_id", help="UUID of the pending strict proposal to approve")
|
|
parser.add_argument("--reviewed-by", required=True, help="reviewer handle to record")
|
|
note = parser.add_mutually_exclusive_group(required=True)
|
|
note.add_argument("--review-note", help="verbatim review note to record")
|
|
note.add_argument("--review-note-file", help="path containing the verbatim review note")
|
|
parser.add_argument("--dry-run", action="store_true", help="print the SQL, do not execute the approval")
|
|
parser.add_argument("--secrets-file", default=ap.DEFAULT_SECRETS_FILE)
|
|
parser.add_argument("--container", default=ap.DEFAULT_CONTAINER)
|
|
parser.add_argument("--db", default=ap.DEFAULT_DB)
|
|
parser.add_argument("--host", default=ap.DEFAULT_HOST)
|
|
parser.add_argument("--role", default=ap.DEFAULT_ROLE)
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(sys.argv[1:] if argv is None else argv)
|
|
review_note = load_review_note(args)
|
|
|
|
password = ap.load_password(args.secrets_file)
|
|
proposal = ap.load_proposal(args, password)
|
|
assert_approvable(proposal, args.reviewed_by, review_note)
|
|
sql = build_approve_sql(args.proposal_id, args.reviewed_by.strip(), review_note)
|
|
|
|
if args.dry_run:
|
|
print(sql)
|
|
return 0
|
|
|
|
out = ap.run_psql(args, sql, password)
|
|
rows = parse_reviewed_rows(out, args.proposal_id)
|
|
print(json.dumps(rows[0], sort_keys=True))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|