From 8d4b216a6f4c673e52101b9d29acfd48276179a4 Mon Sep 17 00:00:00 2001 From: Svrnty Date: Sun, 14 Jun 2026 08:16:03 -0400 Subject: [PATCH] docs: enforce personal-agent profile contract --- AGENT.md | 4 +- CONTRACT.md | 4 +- DISCLOSURE.md | 2 + README.md | 7 +- WORKBOARD.yaml | 10 + docs/STEEV-MASTER.md | 20 ++ ...rsonal-agent-profile-surface-contract.json | 257 ++++++++++++++++++ manifest.yaml | 2 + tools/validate_steev_child.py | 160 ++++++++++- 9 files changed, 458 insertions(+), 8 deletions(-) create mode 100644 docs/STEEV-MASTER.md create mode 100644 docs/contracts/personal-agent-profile-surface-contract.json diff --git a/AGENT.md b/AGENT.md index b8750c5..57242a8 100644 --- a/AGENT.md +++ b/AGENT.md @@ -11,9 +11,11 @@ depends_on: - steev-contract --- +> Supersession note, 2026-06-14: `personal-agent` is the canonical profile identity. Steev is the user-facing display name and current distribution alias. The active profile surface contract is `docs/contracts/personal-agent-profile-surface-contract.json`. + # Steev — Agent Identity -> The WHO of this profile distribution. Loaded conceptually before the orchestrator skill. For the full operating reference, see [`docs/STEEV-MASTER.md`](docs/STEEV-MASTER.md). +> The WHO of this profile distribution. Loaded conceptually before the orchestrator skill. For profile surfaces and effects, use [`docs/contracts/personal-agent-profile-surface-contract.json`](docs/contracts/personal-agent-profile-surface-contract.json). | Field | Value | |---|---| diff --git a/CONTRACT.md b/CONTRACT.md index d420599..a252f60 100644 --- a/CONTRACT.md +++ b/CONTRACT.md @@ -6,12 +6,14 @@ owner: jp source: hand last_reviewed: 2026-05-23 review_by: 2026-08-21 -description: steev profile behavior contract — what Steev does, doesn't do, edge cases. Tier T1 — this file wins for the steev profile. +description: personal-agent behavior contract for the Steev-named distribution; the PACR profile surface contract supersedes older v1 surface assumptions. depends_on: - profile-distribution-protocol note: legacy tier S remapped to T1 per FRONTMATTER-SPEC 2026-05-23. Required fields filled (name, last_reviewed, description) per §7 audit. --- +> Supersession note, 2026-06-14: `personal-agent` is the canonical profile identity. Steev is the user-facing display name and current distribution alias. The active profile surface contract is `docs/contracts/personal-agent-profile-surface-contract.json`. + # Steev — Source of Truth **Role:** Personal Assistant / Chief of Staff for JP (Mathias) diff --git a/DISCLOSURE.md b/DISCLOSURE.md index 0c4537c..b5b7886 100644 --- a/DISCLOSURE.md +++ b/DISCLOSURE.md @@ -13,6 +13,8 @@ description: Canonical disclosure of steev — exposed skills + MCP + sovereign auto_regen_cmd: "yq '.disclosure' manifest.yaml | " --- +> Supersession note, 2026-06-14: this disclosure is historical runtime disclosure for the Steev-named distribution. `personal-agent` is the canonical profile identity. Steev is the display name and current distribution alias. Refresh this disclosure from `docs/contracts/personal-agent-profile-surface-contract.json` before claiming runtime readiness. + # `steev` — Disclosure > Live as of `2026-05-25`. Disclosure schema v2 (manifest `disclosure.schema_version: 2` — adds `external_orchestrators` per DISCLOSURE-SCHEMA §4.6). Source: `steev/manifest.yaml → disclosure:` block. Pre-push hook check 6 (curator/lib/pre-push.sh) enforces this == live `hermes -p steev` runtime. diff --git a/README.md b/README.md index f499783..ffb94fa 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ -# Steev — Hermes profile distribution +# Steev — Personal-Agent Profile Distribution + +`personal-agent` is the canonical profile identity. Steev is the user-facing display name and current distribution alias. JP's personal assistant / chief of staff. Daily briefing, inbox triage, comms in JP's voice, business delegation to CEO. French/English bilingual. - **Identity:** [`AGENT.md`](AGENT.md) — role, mission, boundaries. -- **Full reference (source of truth):** [`docs/STEEV-MASTER.md`](docs/STEEV-MASTER.md). +- **Profile surface contract:** [`docs/contracts/personal-agent-profile-surface-contract.json`](docs/contracts/personal-agent-profile-surface-contract.json) — canonical surfaces, effects, memory route, and proof policy. +- **Historical Steev reference redirect:** [`docs/STEEV-MASTER.md`](docs/STEEV-MASTER.md). ## Structure diff --git a/WORKBOARD.yaml b/WORKBOARD.yaml index 9be1b21..04db872 100644 --- a/WORKBOARD.yaml +++ b/WORKBOARD.yaml @@ -9,3 +9,13 @@ items: status: complete source: docs/prd/2026-06-14-personal-agent-context-runtime-prd.md owner: jp + - id: PACR-001 + title: Personal-Agent Profile Authority And Surface Contract + status: complete + source: docs/contracts/personal-agent-profile-surface-contract.json + owner: jp + - id: PACR-002 + title: Supersession And Graph Hygiene Register Validator Gate + status: complete + source: docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md + owner: jp diff --git a/docs/STEEV-MASTER.md b/docs/STEEV-MASTER.md new file mode 100644 index 0000000..f08fcfa --- /dev/null +++ b/docs/STEEV-MASTER.md @@ -0,0 +1,20 @@ +--- +name: steev-master-supersession-redirect +status: superseded +owner: jp +source: personal-agent-context-runtime +last_reviewed: 2026-06-14 +description: Redirect from the historical Steev master reference to the active personal-agent profile surface contract. +--- + +# Steev Master Supersession + +`personal-agent` is the canonical profile identity. Steev is the user-facing display name and current distribution alias. + +Active authority: + +- `docs/contracts/personal-agent-profile-surface-contract.json` +- `docs/prd/2026-06-14-personal-agent-context-runtime-prd.md` +- `docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md` + +This file exists so older references do not become graph ambiguity. diff --git a/docs/contracts/personal-agent-profile-surface-contract.json b/docs/contracts/personal-agent-profile-surface-contract.json new file mode 100644 index 0000000..c260fe3 --- /dev/null +++ b/docs/contracts/personal-agent-profile-surface-contract.json @@ -0,0 +1,257 @@ +{ + "schema_version": "personal-agent-profile-surface-contract/v1", + "profile_identity": "personal-agent", + "display_name": "Steev", + "distribution_alias": "steev", + "owner": "jp", + "status": "active-authority", + "authority_note": "personal-agent is the profile identity. Steev is the user-facing display name and current distribution alias.", + "memory_policy": { + "allowed_target": "secondbrain-personal", + "forbidden_targets": [ + "orgbrain" + ], + "durable_write_policy": "proposal-only-until-governed-secondbrain-curator-apply-route", + "proof_policy": "redacted-only" + }, + "credential_policy": { + "mode": "keyvault-reference-names-only", + "forbidden_in_core_or_proof": [ + "credential_values", + "secret_values", + "session_cookies", + "keychain_values", + "password_manager_values" + ] + }, + "proof_redaction_policy": { + "forbidden_in_core_or_proof": [ + "raw_messages", + "mail_bodies", + "contact_details", + "calendar_event_details", + "drive_file_names", + "endpoint_payloads", + "credentials", + "cookies", + "keychain_values", + "password_manager_values", + "secret_values" + ] + }, + "readiness_states": [ + "ready", + "degraded", + "pending", + "blocked", + "disabled" + ], + "surfaces": [ + { + "name": "imessage.read", + "capability_package": "bluebubbles", + "package_surface": "bluebubbles.imessage.readonly", + "status": "active-capability-package", + "allowed_effects": [ + "read_message_stream", + "read_conversation_history", + "emit_redacted_health", + "emit_secondbrain_personal_proposal" + ], + "denied_effects": [ + "send_message", + "delete_message", + "mark_read", + "read_receipt", + "contact_mutation", + "chat_mutation", + "attachment_download", + "orgbrain_write" + ], + "confirmation": "not-applicable-read-only" + }, + { + "name": "mail.read", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "read_mail_metadata", + "read_mail_body_when_user_requested", + "search_mail", + "emit_redacted_health", + "emit_secondbrain_personal_proposal" + ], + "denied_effects": [ + "send_mail", + "delete_mail", + "archive_mail", + "mark_read", + "mark_unread", + "orgbrain_write" + ], + "confirmation": "not-applicable-read" + }, + { + "name": "mail.draft", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "compose_draft_for_user_review" + ], + "denied_effects": [ + "send_mail", + "mutate_mailbox", + "orgbrain_write" + ], + "confirmation": "user-review-before-send" + }, + { + "name": "mail.send_with_confirmation", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "send_mail_after_explicit_confirmation" + ], + "denied_effects": [ + "send_without_confirmation", + "bulk_send", + "background_send", + "orgbrain_write" + ], + "confirmation": "explicit-jp-confirmation-required" + }, + { + "name": "calendar.read", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "read_calendar_metadata", + "read_event_detail_when_user_requested", + "search_calendar", + "emit_redacted_health", + "emit_secondbrain_personal_proposal" + ], + "denied_effects": [ + "create_event", + "update_event", + "delete_event", + "orgbrain_write" + ], + "confirmation": "not-applicable-read" + }, + { + "name": "calendar.propose_event", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "draft_calendar_change_for_user_review" + ], + "denied_effects": [ + "write_calendar", + "delete_event", + "orgbrain_write" + ], + "confirmation": "user-review-before-write" + }, + { + "name": "calendar.write_with_confirmation", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "create_event_after_explicit_confirmation", + "update_event_after_explicit_confirmation" + ], + "denied_effects": [ + "write_without_confirmation", + "delete_event", + "orgbrain_write" + ], + "confirmation": "explicit-jp-confirmation-required" + }, + { + "name": "contacts.read", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "read_contact_metadata", + "read_contact_detail_when_user_requested", + "search_contacts", + "emit_redacted_health", + "emit_secondbrain_personal_proposal" + ], + "denied_effects": [ + "create_contact", + "update_contact", + "delete_contact", + "orgbrain_write" + ], + "confirmation": "not-applicable-read" + }, + { + "name": "contacts.write_with_confirmation", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "create_contact_after_explicit_confirmation", + "update_contact_after_explicit_confirmation" + ], + "denied_effects": [ + "write_without_confirmation", + "delete_contact", + "orgbrain_write" + ], + "confirmation": "explicit-jp-confirmation-required" + }, + { + "name": "drive.read", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "read_drive_metadata_when_user_requested", + "read_file_when_user_requested", + "emit_redacted_health", + "emit_secondbrain_personal_proposal" + ], + "denied_effects": [ + "write_file", + "move_file", + "copy_file", + "delete_file", + "purge_directory", + "orgbrain_write" + ], + "confirmation": "not-applicable-read" + }, + { + "name": "drive.write_with_confirmation", + "capability_package": "proton-rclone", + "status": "blocked-follow-up", + "allowed_effects": [ + "write_file_after_explicit_confirmation", + "move_file_after_explicit_confirmation", + "copy_file_after_explicit_confirmation" + ], + "denied_effects": [ + "write_without_confirmation", + "delete_file", + "purge_directory", + "orgbrain_write" + ], + "confirmation": "explicit-jp-confirmation-required" + }, + { + "name": "browser.host_runtime.full_control", + "capability_package": "mac-mini-host-runtime", + "status": "blocked-follow-up", + "allowed_effects": [], + "denied_effects": [ + "browser_full_control_without_hitl_approval", + "read_password_manager", + "export_cookies", + "read_keychain", + "orgbrain_write" + ], + "confirmation": "separate-hitl-host-runtime-approval-required" + } + ] +} diff --git a/manifest.yaml b/manifest.yaml index 0505fd5..d5f5ccf 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -2,6 +2,8 @@ # Read by install.sh. Convention shared by all Hermes profile distributions # (see ../sot/03-PROTOCOLS/PROFILE-DISTRIBUTION-PROTOCOL.md — the canonical protocol). profile: steev # Hermes profile name (personal — no org suffix per FRAMEWORK §6.1) +profile_identity: personal-agent # canonical profile identity; Steev is display/distribution alias. +display_name: Steev kind: profile-distribution # family marker; steev = personal-assistant reference impl role: personal-assistant # function — Chief of Staff for one principal (JP) # org: ~ # intentionally omitted — steev is personal/agnostic diff --git a/tools/validate_steev_child.py b/tools/validate_steev_child.py index 5a45c79..ad39f26 100755 --- a/tools/validate_steev_child.py +++ b/tools/validate_steev_child.py @@ -1,12 +1,72 @@ #!/usr/bin/env python3 -"""Validate Steev Profile child workspace shell.""" +"""Validate the Steev-named personal-agent profile distribution.""" from __future__ import annotations import json from pathlib import Path +import yaml + ROOT = Path(__file__).resolve().parents[1] -REQUIRED = ["AGENTS.md", "README.md", "WORKBOARD.yaml"] +REQUIRED = [ + "AGENTS.md", + "README.md", + "WORKBOARD.yaml", + "manifest.yaml", + "AGENT.md", + "CONTRACT.md", + "DISCLOSURE.md", + "docs/STEEV-MASTER.md", + "docs/contracts/personal-agent-profile-surface-contract.json", + "docs/prd/2026-06-14-personal-agent-context-runtime-prd.md", + "docs/issues/2026-06-14-personal-agent-context-runtime-work-orders.md", + "docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md", +] + +REQUIRED_SURFACES = { + "imessage.read", + "mail.read", + "mail.draft", + "mail.send_with_confirmation", + "calendar.read", + "calendar.propose_event", + "calendar.write_with_confirmation", + "contacts.read", + "contacts.write_with_confirmation", + "drive.read", + "drive.write_with_confirmation", + "browser.host_runtime.full_control", +} + +REQUIRED_REDACTION_TERMS = { + "raw_messages", + "mail_bodies", + "contact_details", + "calendar_event_details", + "drive_file_names", + "endpoint_payloads", + "credentials", + "cookies", + "keychain_values", + "password_manager_values", + "secret_values", +} + + +def read_text(rel: str) -> str: + return (ROOT / rel).read_text(encoding="utf-8") + + +def load_contract(errors: list[str]) -> dict: + rel = "docs/contracts/personal-agent-profile-surface-contract.json" + path = ROOT / rel + if not path.exists(): + return {} + try: + return json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + errors.append(f"contract_json_invalid:{rel}:{exc.lineno}:{exc.colno}") + return {} def main() -> int: @@ -17,7 +77,7 @@ def main() -> int: board = ROOT / "WORKBOARD.yaml" if board.exists(): text = board.read_text(encoding="utf-8") - for snippet in ["STEEV-WORK-001", "status: candidate", "owner: jp"]: + for snippet in ["STEEV-WORK-001", "PACR-001", "PACR-002", "status: candidate", "owner: jp"]: if snippet not in text: errors.append(f"workboard_missing:{snippet}") agents = ROOT / "AGENTS.md" @@ -26,7 +86,99 @@ def main() -> int: for snippet in ["child-local", "not Cortex OS Core authority", "python3 tools/validate_steev_child.py"]: if snippet not in text: errors.append(f"agents_missing:{snippet}") - result = {"ok": not errors, "validator": "steev-child-v1", "checked": REQUIRED, "errors": errors, "warnings": []} + + manifest = ROOT / "manifest.yaml" + if manifest.exists(): + data = yaml.safe_load(manifest.read_text(encoding="utf-8")) or {} + if data.get("profile_identity") != "personal-agent": + errors.append("manifest_profile_identity_not_personal_agent") + if data.get("display_name") != "Steev": + errors.append("manifest_display_name_not_steev") + if data.get("profile") != "steev": + errors.append("manifest_distribution_alias_not_steev") + + contract = load_contract(errors) + if contract: + if contract.get("profile_identity") != "personal-agent": + errors.append("contract_profile_identity_not_personal_agent") + if contract.get("display_name") != "Steev": + errors.append("contract_display_name_not_steev") + if contract.get("distribution_alias") != "steev": + errors.append("contract_distribution_alias_not_steev") + memory = contract.get("memory_policy", {}) + if memory.get("allowed_target") != "secondbrain-personal": + errors.append("contract_memory_allowed_target_not_secondbrain_personal") + if "orgbrain" not in memory.get("forbidden_targets", []): + errors.append("contract_memory_orgbrain_not_forbidden") + if "proposal-only" not in memory.get("durable_write_policy", ""): + errors.append("contract_memory_not_proposal_only") + credential = contract.get("credential_policy", {}) + if credential.get("mode") != "keyvault-reference-names-only": + errors.append("contract_credential_policy_not_keyvault_refs_only") + redaction = set(contract.get("proof_redaction_policy", {}).get("forbidden_in_core_or_proof", [])) + missing_redaction = sorted(REQUIRED_REDACTION_TERMS - redaction) + if missing_redaction: + errors.append(f"contract_redaction_missing:{','.join(missing_redaction)}") + states = set(contract.get("readiness_states", [])) + for state in ["ready", "degraded", "pending", "blocked", "disabled"]: + if state not in states: + errors.append(f"contract_readiness_state_missing:{state}") + surfaces = contract.get("surfaces", []) + surface_names = {surface.get("name") for surface in surfaces} + missing_surfaces = sorted(REQUIRED_SURFACES - surface_names) + if missing_surfaces: + errors.append(f"contract_surfaces_missing:{','.join(missing_surfaces)}") + for surface in surfaces: + name = surface.get("name", "") + if not surface.get("capability_package"): + errors.append(f"surface_missing_capability_package:{name}") + if not isinstance(surface.get("allowed_effects"), list): + errors.append(f"surface_allowed_effects_not_list:{name}") + denied = surface.get("denied_effects") + if not isinstance(denied, list): + errors.append(f"surface_denied_effects_not_list:{name}") + elif "orgbrain_write" not in denied: + errors.append(f"surface_orgbrain_write_not_denied:{name}") + if not surface.get("confirmation"): + errors.append(f"surface_missing_confirmation_policy:{name}") + + for rel in ["AGENT.md", "CONTRACT.md", "DISCLOSURE.md", "README.md", "docs/STEEV-MASTER.md"]: + path = ROOT / rel + if not path.exists(): + continue + text = path.read_text(encoding="utf-8") + if "personal-agent" not in text: + errors.append(f"visible_personal_agent_note_missing:{rel}") + if "display name" not in text and "display/distribution alias" not in text: + errors.append(f"visible_display_alias_note_missing:{rel}") + + supersession = ROOT / "docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md" + if supersession.exists(): + text = supersession.read_text(encoding="utf-8") + for snippet in [ + "active-authority", + "active-alias", + "active-capability-package", + "superseded", + "legacy-reference", + "blocked-follow-up", + "secondbrain-personal", + "`orgbrain` as denied", + "PACR-010", + ]: + if snippet not in text: + errors.append(f"supersession_missing:{snippet}") + for stale in ["SPCR-", "steev-personal-context-runtime"]: + if stale in text: + errors.append(f"supersession_stale_reference:{stale}") + + result = { + "ok": not errors, + "validator": "personal-agent-profile-distribution-v2", + "checked": REQUIRED, + "errors": errors, + "warnings": [], + } print(json.dumps(result, indent=2, sort_keys=True)) return 0 if result["ok"] else 1