From 412f669b937a2ccff28a6fc650bb15912875d1ea Mon Sep 17 00:00:00 2001 From: Svrnty Date: Sun, 14 Jun 2026 08:36:34 -0400 Subject: [PATCH] docs: hand off personal-agent services to conductor curator --- README.md | 1 + WORKBOARD.yaml | 5 + docs/STEEV-MASTER.md | 1 + ...ent-conductor-curator-service-handoff.json | 202 ++++++++++++++++++ ...t-context-runtime-supersession-register.md | 4 +- tools/validate_steev_child.py | 165 +++++++++++++- 6 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 docs/contracts/personal-agent-conductor-curator-service-handoff.json diff --git a/README.md b/README.md index 9a61175..246ad81 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ JP's personal assistant / chief of staff. Daily briefing, inbox triage, comms in - **BlueBubbles binding:** [`docs/contracts/personal-agent-bluebubbles-binding.json`](docs/contracts/personal-agent-bluebubbles-binding.json) — `imessage.read` binds to the existing BlueBubbles package without a duplicate connector. - **Proton/rclone package candidate:** [`docs/contracts/personal-agent-proton-rclone-package.json`](docs/contracts/personal-agent-proton-rclone-package.json) — Mail, Calendar, Contacts, and Drive surfaces with redacted runtime inventory and no readiness overclaim. - **Secondbrain proposal route:** [`docs/contracts/personal-agent-secondbrain-proposal-route.json`](docs/contracts/personal-agent-secondbrain-proposal-route.json) — proposal-only personal memory intake; durable apply remains owned by Secondbrain. +- **Conductor/Curator service handoff:** [`docs/contracts/personal-agent-conductor-curator-service-handoff.json`](docs/contracts/personal-agent-conductor-curator-service-handoff.json) — redacted service map for future route selection and hygiene review pickup. - **Historical Steev reference redirect:** [`docs/STEEV-MASTER.md`](docs/STEEV-MASTER.md). ## Structure diff --git a/WORKBOARD.yaml b/WORKBOARD.yaml index fef37f8..5774763 100644 --- a/WORKBOARD.yaml +++ b/WORKBOARD.yaml @@ -34,3 +34,8 @@ items: status: complete source: docs/contracts/personal-agent-secondbrain-proposal-route.json owner: jp + - id: PACR-006 + title: Conductor And Curator Service Handoff + status: complete + source: docs/contracts/personal-agent-conductor-curator-service-handoff.json + owner: jp diff --git a/docs/STEEV-MASTER.md b/docs/STEEV-MASTER.md index 16fd1e1..c2efde0 100644 --- a/docs/STEEV-MASTER.md +++ b/docs/STEEV-MASTER.md @@ -17,6 +17,7 @@ Active authority: - `docs/contracts/personal-agent-bluebubbles-binding.json` - `docs/contracts/personal-agent-proton-rclone-package.json` - `docs/contracts/personal-agent-secondbrain-proposal-route.json` +- `docs/contracts/personal-agent-conductor-curator-service-handoff.json` - `docs/prd/2026-06-14-personal-agent-context-runtime-prd.md` - `docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md` diff --git a/docs/contracts/personal-agent-conductor-curator-service-handoff.json b/docs/contracts/personal-agent-conductor-curator-service-handoff.json new file mode 100644 index 0000000..564a0c9 --- /dev/null +++ b/docs/contracts/personal-agent-conductor-curator-service-handoff.json @@ -0,0 +1,202 @@ +{ + "schema_version": "personal-agent-conductor-curator-service-handoff/v1", + "status": "active-profile-service-handoff", + "handoff_id": "personal-agent-conductor-curator-service-handoff", + "profile_identity": "personal-agent", + "display_name": "Steev", + "observed_date": "2026-06-14", + "core_promotion_claimed": false, + "seed_readiness_claimed": false, + "runtime_readiness_claimed": false, + "desktop_integration_claimed": false, + "authority_boundary": { + "profile_owns_capability_surface_contract": true, + "conductor_owns_future_route_selection_and_worker_contracts": true, + "curator_owns_future_hygiene_review_queue": true, + "secondbrain_owns_personal_memory_domain_apply": true, + "capability_packages_own_runtime_health": true, + "notes": "This handoff gives Conductor and Curator a redacted service map. It does not mutate those workspaces or claim they have adopted it." + }, + "memory_policy": { + "target": "secondbrain-personal", + "target_domain_term": "Personal Memory Domain", + "forbidden": [ + "orgbrain" + ], + "durable_write_policy": "proposal-only-until-governed-secondbrain-curator-apply-route" + }, + "service_identities": [ + { + "service_id": "personal-agent.imessage.bluebubbles.readonly", + "capability_package": "bluebubbles", + "owner_route": "bluebubbles", + "surface": "imessage.read", + "health_shape": "redacted-readonly-runtime-health", + "readiness_state": "package-ready-profile-bound", + "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", + "attachment_download", + "orgbrain_write", + "durable_memory_write" + ] + }, + { + "service_id": "personal-agent.proton-rclone.package-candidate", + "capability_package": "proton-rclone", + "owner_route": "steev", + "surface": "mail.calendar.contacts.drive", + "health_shape": "redacted-per-surface-runtime-inventory", + "readiness_state": "degraded-package-candidate", + "allowed_effects": [ + "emit_mail_health", + "emit_calendar_health", + "emit_contacts_health", + "emit_drive_health", + "emit_secondbrain_personal_proposal" + ], + "denied_effects": [ + "send_without_confirmation", + "calendar_write_without_confirmation", + "contact_mutation_without_confirmation", + "drive_write_without_confirmation", + "drive_delete", + "orgbrain_write", + "durable_memory_write" + ] + }, + { + "service_id": "personal-agent.secondbrain.proposal-route", + "capability_package": "personal-agent-profile", + "owner_route": "steev", + "surface": "secondbrain.memory.proposal", + "health_shape": "redacted-proposal-envelope-contract", + "readiness_state": "profile-contract-ready-apply-blocked", + "allowed_effects": [ + "emit_redacted_proposal", + "emit_source_handle", + "emit_content_digest" + ], + "denied_effects": [ + "secondbrain_apply", + "direct_memory_write", + "orgbrain_write", + "raw_payload_in_proof" + ] + } + ], + "conductor_handoff": { + "target_workspace": "../conductor", + "target_role": "future-route-selection-and-worker-contract-owner", + "adoption_status": "pending-conductor-lane-pickup", + "validator_command": "python3 tools/validate_conductor_child.py", + "validator_result_observed": "ok", + "required_route_decision_inputs": [ + "profile_identity", + "requested_surface", + "capability_package", + "owner_route", + "allowed_effects", + "denied_effects", + "validator_command", + "evidence_expectation", + "approval_required" + ], + "forbidden_conductor_effects": [ + "runtime_start", + "runtime_stop", + "desktop_integration", + "core_mutation", + "seed_completion_claim", + "secret_read", + "raw_payload_import", + "sibling_mutation_without_worker_route" + ], + "worker_contract_expectations": [ + "one route per worker", + "one bounded goal", + "workspace-local validator", + "redacted evidence", + "no raw personal payloads", + "no unapproved runtime lifecycle" + ] + }, + "curator_handoff": { + "target_workspace": "../curator", + "target_role": "future-personal-memory-hygiene-review-queue", + "adoption_status": "pending-curator-lane-pickup", + "validator_command": "python3 tools/validate_curator_child.py", + "validator_result_observed": "ok", + "allowed_future_reviews": [ + "novelty_candidate", + "stale_candidate", + "duplicate_candidate", + "supersession_candidate", + "archive_candidate", + "provenance_gap", + "contradiction_candidate" + ], + "forbidden_curator_effects": [ + "direct_memory_write", + "raw_payload_import", + "orgbrain_write", + "profile_exposure_broadening", + "runtime_activation", + "secret_read", + "core_mutation", + "seed_mutation" + ] + }, + "apply_expectations": { + "target": "secondbrain-personal", + "apply_owner": "secondbrain", + "hygiene_owner": "curator", + "apply_allowed_now": false, + "requires_proposal_envelope": true, + "requires_approval": true, + "requires_secondbrain_validator": "python3 tools/validate_secondbrain_child.py", + "requires_redacted_evidence": true, + "forbidden_targets": [ + "orgbrain" + ] + }, + "source_contracts": [ + "docs/contracts/personal-agent-profile-surface-contract.json", + "docs/contracts/personal-agent-bluebubbles-binding.json", + "docs/contracts/personal-agent-proton-rclone-package.json", + "docs/contracts/personal-agent-secondbrain-proposal-route.json" + ], + "proof_policy": { + "mode": "redacted-only", + "forbidden_fields": [ + "raw_messages", + "message_text", + "mail_bodies", + "mail_subjects", + "contact_details", + "calendar_event_details", + "drive_file_names", + "drive_file_contents", + "endpoint_payloads", + "credentials", + "secret_values", + "raw_transcripts" + ] + }, + "remaining_gates": { + "conductor_lane_pickup": "blocked-follow-up", + "curator_personal_memory_hygiene_lane_pickup": "blocked-follow-up", + "secondbrain_durable_apply": "blocked-follow-up", + "runtime_health_proof": "blocked-follow-up", + "desktop_adapter_exposure": "blocked-follow-up", + "seed_package_pickup": "blocked-follow-up" + } +} diff --git a/docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md b/docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md index b853e74..07fac33 100644 --- a/docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md +++ b/docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md @@ -38,7 +38,8 @@ desktop exposure must be treated as one of: | Proton Drive/rclone | blocked-follow-up | rclone read probe is redacted-ok; governed wrapper and write gates remain follow-up work | | Personal-agent Secondbrain proposal route | active-authority | `docs/contracts/personal-agent-secondbrain-proposal-route.json` defines proposal-only personal memory intake | | Personal memory durable apply | blocked-follow-up | Owning Secondbrain/curator route must approve and apply; profile/capability packages do not write durable memory | -| Conductor/curator service routing | blocked-follow-up | `PACR-006`, after owning lanes release | +| Personal-agent Conductor/Curator service handoff | active-authority | `docs/contracts/personal-agent-conductor-curator-service-handoff.json` gives route and hygiene lanes a redacted service map | +| Conductor/curator adoption | blocked-follow-up | Owning lanes must explicitly pick up the handoff; this profile does not mutate them | | Desktop app exposure | blocked-follow-up | `PACR-008`, after adapter lane release | | Browser/Webwright host control | blocked-follow-up | `PACR-009`, explicit approval only | @@ -76,6 +77,7 @@ desktop exposure must be treated as one of: - Graph context should expose BlueBubbles as the active iMessage capability package. - Graph context should expose the Proton/rclone package candidate as the active standardization pickup, not a runtime-ready child package. - Graph context should expose the personal-agent Secondbrain proposal route as active while keeping durable apply blocked to Secondbrain/curator. +- Graph context should expose the personal-agent Conductor/Curator service handoff as active, while adoption remains blocked to owning lanes. - Graph context should not treat legacy Cortex Proton/rclone repositories as active authority. - Graph context should not treat duplicate Proton skills as separate current product surfaces. - Graph context should mark browser/Webwright host control as separate HITL runtime authority. diff --git a/tools/validate_steev_child.py b/tools/validate_steev_child.py index f173b87..84f7ce2 100755 --- a/tools/validate_steev_child.py +++ b/tools/validate_steev_child.py @@ -21,6 +21,7 @@ REQUIRED = [ "docs/contracts/personal-agent-bluebubbles-binding.json", "docs/contracts/personal-agent-proton-rclone-package.json", "docs/contracts/personal-agent-secondbrain-proposal-route.json", + "docs/contracts/personal-agent-conductor-curator-service-handoff.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", @@ -105,6 +106,12 @@ REQUIRED_PROPOSAL_ENVELOPE_FIELDS = { "proof_redaction", } +REQUIRED_HANDOFF_SERVICES = { + "personal-agent.imessage.bluebubbles.readonly", + "personal-agent.proton-rclone.package-candidate", + "personal-agent.secondbrain.proposal-route", +} + def read_text(rel: str) -> str: return (ROOT / rel).read_text(encoding="utf-8") @@ -140,6 +147,7 @@ def main() -> int: "PACR-003", "PACR-004", "PACR-005", + "PACR-006", "status: candidate", "owner: jp", ]: @@ -577,6 +585,160 @@ def main() -> int: if remaining_gates.get(gate) != "blocked-follow-up": errors.append(f"memory_route_remaining_gate_missing:{gate}") + service_handoff = load_json("docs/contracts/personal-agent-conductor-curator-service-handoff.json", errors) + if service_handoff: + if service_handoff.get("schema_version") != "personal-agent-conductor-curator-service-handoff/v1": + errors.append("service_handoff_schema_version_invalid") + if service_handoff.get("status") != "active-profile-service-handoff": + errors.append("service_handoff_status_invalid") + if service_handoff.get("handoff_id") != "personal-agent-conductor-curator-service-handoff": + errors.append("service_handoff_id_invalid") + if service_handoff.get("profile_identity") != "personal-agent": + errors.append("service_handoff_profile_identity_not_personal_agent") + if service_handoff.get("display_name") != "Steev": + errors.append("service_handoff_display_name_not_steev") + for key in [ + "core_promotion_claimed", + "seed_readiness_claimed", + "runtime_readiness_claimed", + "desktop_integration_claimed", + ]: + if service_handoff.get(key) is not False: + errors.append(f"service_handoff_overclaim:{key}") + boundary = service_handoff.get("authority_boundary", {}) + for key in [ + "profile_owns_capability_surface_contract", + "conductor_owns_future_route_selection_and_worker_contracts", + "curator_owns_future_hygiene_review_queue", + "secondbrain_owns_personal_memory_domain_apply", + "capability_packages_own_runtime_health", + ]: + if boundary.get(key) is not True: + errors.append(f"service_handoff_boundary_missing:{key}") + memory = service_handoff.get("memory_policy", {}) + if memory.get("target") != "secondbrain-personal": + errors.append("service_handoff_memory_target_not_secondbrain_personal") + if memory.get("target_domain_term") != "Personal Memory Domain": + errors.append("service_handoff_memory_domain_term_invalid") + if "orgbrain" not in memory.get("forbidden", []): + errors.append("service_handoff_orgbrain_not_forbidden") + if "proposal-only" not in memory.get("durable_write_policy", ""): + errors.append("service_handoff_memory_not_proposal_only") + services = service_handoff.get("service_identities", []) + service_ids = {service.get("service_id") for service in services} + missing_services = sorted(REQUIRED_HANDOFF_SERVICES - service_ids) + if missing_services: + errors.append(f"service_handoff_services_missing:{','.join(missing_services)}") + for service in services: + service_id = service.get("service_id", "") + if not service.get("capability_package"): + errors.append(f"service_handoff_service_package_missing:{service_id}") + if not service.get("owner_route"): + errors.append(f"service_handoff_service_owner_missing:{service_id}") + if not service.get("health_shape"): + errors.append(f"service_handoff_service_health_missing:{service_id}") + if not service.get("readiness_state"): + errors.append(f"service_handoff_service_readiness_missing:{service_id}") + allowed = service.get("allowed_effects") + denied = service.get("denied_effects") + if not isinstance(allowed, list) or not allowed: + errors.append(f"service_handoff_allowed_effects_invalid:{service_id}") + if not isinstance(denied, list): + errors.append(f"service_handoff_denied_effects_invalid:{service_id}") + else: + for effect in ["orgbrain_write", "durable_memory_write"]: + if effect not in denied and service_id != "personal-agent.secondbrain.proposal-route": + errors.append(f"service_handoff_denied_effect_missing:{service_id}:{effect}") + if service_id == "personal-agent.secondbrain.proposal-route": + for effect in ["secondbrain_apply", "direct_memory_write", "orgbrain_write"]: + if effect not in denied: + errors.append(f"service_handoff_denied_effect_missing:{service_id}:{effect}") + conductor = service_handoff.get("conductor_handoff", {}) + if conductor.get("target_workspace") != "../conductor": + errors.append("service_handoff_conductor_workspace_invalid") + if conductor.get("adoption_status") != "pending-conductor-lane-pickup": + errors.append("service_handoff_conductor_adoption_not_pending") + if conductor.get("validator_command") != "python3 tools/validate_conductor_child.py": + errors.append("service_handoff_conductor_validator_missing") + if conductor.get("validator_result_observed") != "ok": + errors.append("service_handoff_conductor_validator_not_ok") + conductor_forbidden = set(conductor.get("forbidden_conductor_effects", [])) + for effect in ["runtime_start", "runtime_stop", "desktop_integration", "core_mutation", "secret_read", "raw_payload_import"]: + if effect not in conductor_forbidden: + errors.append(f"service_handoff_conductor_forbidden_missing:{effect}") + worker_expectations = set(conductor.get("worker_contract_expectations", [])) + for expectation in ["one route per worker", "workspace-local validator", "redacted evidence", "no raw personal payloads"]: + if expectation not in worker_expectations: + errors.append(f"service_handoff_worker_expectation_missing:{expectation}") + curator = service_handoff.get("curator_handoff", {}) + if curator.get("target_workspace") != "../curator": + errors.append("service_handoff_curator_workspace_invalid") + if curator.get("adoption_status") != "pending-curator-lane-pickup": + errors.append("service_handoff_curator_adoption_not_pending") + if curator.get("validator_command") != "python3 tools/validate_curator_child.py": + errors.append("service_handoff_curator_validator_missing") + if curator.get("validator_result_observed") != "ok": + errors.append("service_handoff_curator_validator_not_ok") + curator_reviews = set(curator.get("allowed_future_reviews", [])) + for review in ["novelty_candidate", "stale_candidate", "duplicate_candidate", "supersession_candidate", "archive_candidate"]: + if review not in curator_reviews: + errors.append(f"service_handoff_curator_review_missing:{review}") + curator_forbidden = set(curator.get("forbidden_curator_effects", [])) + for effect in ["direct_memory_write", "raw_payload_import", "orgbrain_write", "secret_read", "core_mutation", "seed_mutation"]: + if effect not in curator_forbidden: + errors.append(f"service_handoff_curator_forbidden_missing:{effect}") + apply = service_handoff.get("apply_expectations", {}) + if apply.get("target") != "secondbrain-personal": + errors.append("service_handoff_apply_target_invalid") + if apply.get("apply_owner") != "secondbrain": + errors.append("service_handoff_apply_owner_invalid") + if apply.get("hygiene_owner") != "curator": + errors.append("service_handoff_hygiene_owner_invalid") + if apply.get("apply_allowed_now") is not False: + errors.append("service_handoff_apply_allowed_now") + for key in ["requires_proposal_envelope", "requires_approval", "requires_redacted_evidence"]: + if apply.get(key) is not True: + errors.append(f"service_handoff_apply_requirement_missing:{key}") + if apply.get("requires_secondbrain_validator") != "python3 tools/validate_secondbrain_child.py": + errors.append("service_handoff_secondbrain_validator_missing") + if "orgbrain" not in apply.get("forbidden_targets", []): + errors.append("service_handoff_apply_orgbrain_not_forbidden") + source_contracts = set(service_handoff.get("source_contracts", [])) + for ref in [ + "docs/contracts/personal-agent-profile-surface-contract.json", + "docs/contracts/personal-agent-bluebubbles-binding.json", + "docs/contracts/personal-agent-proton-rclone-package.json", + "docs/contracts/personal-agent-secondbrain-proposal-route.json", + ]: + if ref not in source_contracts: + errors.append(f"service_handoff_source_contract_missing:{ref}") + forbidden_fields = set(service_handoff.get("proof_policy", {}).get("forbidden_fields", [])) + for field in [ + "raw_messages", + "message_text", + "mail_bodies", + "contact_details", + "calendar_event_details", + "drive_file_names", + "endpoint_payloads", + "credentials", + "secret_values", + "raw_transcripts", + ]: + if field not in forbidden_fields: + errors.append(f"service_handoff_forbidden_field_missing:{field}") + remaining_gates = service_handoff.get("remaining_gates", {}) + for gate in [ + "conductor_lane_pickup", + "curator_personal_memory_hygiene_lane_pickup", + "secondbrain_durable_apply", + "runtime_health_proof", + "desktop_adapter_exposure", + "seed_package_pickup", + ]: + if remaining_gates.get(gate) != "blocked-follow-up": + errors.append(f"service_handoff_remaining_gate_missing:{gate}") + for rel in ["AGENT.md", "CONTRACT.md", "DISCLOSURE.md", "README.md", "docs/STEEV-MASTER.md"]: path = ROOT / rel if not path.exists(): @@ -597,6 +759,7 @@ def main() -> int: "Personal-agent BlueBubbles binding", "Proton/rclone package candidate", "Personal-agent Secondbrain proposal route", + "Personal-agent Conductor/Curator service handoff", "superseded", "legacy-reference", "blocked-follow-up", @@ -612,7 +775,7 @@ def main() -> int: result = { "ok": not errors, - "validator": "personal-agent-profile-distribution-v4", + "validator": "personal-agent-profile-distribution-v5", "checked": REQUIRED, "errors": errors, "warnings": [],