docs: hand off personal-agent services to conductor curator
This commit is contained in:
@@ -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.
|
- **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.
|
- **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.
|
- **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).
|
- **Historical Steev reference redirect:** [`docs/STEEV-MASTER.md`](docs/STEEV-MASTER.md).
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|||||||
@@ -34,3 +34,8 @@ items:
|
|||||||
status: complete
|
status: complete
|
||||||
source: docs/contracts/personal-agent-secondbrain-proposal-route.json
|
source: docs/contracts/personal-agent-secondbrain-proposal-route.json
|
||||||
owner: jp
|
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
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Active authority:
|
|||||||
- `docs/contracts/personal-agent-bluebubbles-binding.json`
|
- `docs/contracts/personal-agent-bluebubbles-binding.json`
|
||||||
- `docs/contracts/personal-agent-proton-rclone-package.json`
|
- `docs/contracts/personal-agent-proton-rclone-package.json`
|
||||||
- `docs/contracts/personal-agent-secondbrain-proposal-route.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/prd/2026-06-14-personal-agent-context-runtime-prd.md`
|
||||||
- `docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md`
|
- `docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md`
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -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 |
|
| 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-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 |
|
| 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 |
|
| Desktop app exposure | blocked-follow-up | `PACR-008`, after adapter lane release |
|
||||||
| Browser/Webwright host control | blocked-follow-up | `PACR-009`, explicit approval only |
|
| 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 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 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 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 legacy Cortex Proton/rclone repositories as active authority.
|
||||||
- Graph context should not treat duplicate Proton skills as separate current product surfaces.
|
- 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.
|
- Graph context should mark browser/Webwright host control as separate HITL runtime authority.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ REQUIRED = [
|
|||||||
"docs/contracts/personal-agent-bluebubbles-binding.json",
|
"docs/contracts/personal-agent-bluebubbles-binding.json",
|
||||||
"docs/contracts/personal-agent-proton-rclone-package.json",
|
"docs/contracts/personal-agent-proton-rclone-package.json",
|
||||||
"docs/contracts/personal-agent-secondbrain-proposal-route.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/prd/2026-06-14-personal-agent-context-runtime-prd.md",
|
||||||
"docs/issues/2026-06-14-personal-agent-context-runtime-work-orders.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",
|
"docs/supersession/2026-06-14-personal-agent-context-runtime-supersession-register.md",
|
||||||
@@ -105,6 +106,12 @@ REQUIRED_PROPOSAL_ENVELOPE_FIELDS = {
|
|||||||
"proof_redaction",
|
"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:
|
def read_text(rel: str) -> str:
|
||||||
return (ROOT / rel).read_text(encoding="utf-8")
|
return (ROOT / rel).read_text(encoding="utf-8")
|
||||||
@@ -140,6 +147,7 @@ def main() -> int:
|
|||||||
"PACR-003",
|
"PACR-003",
|
||||||
"PACR-004",
|
"PACR-004",
|
||||||
"PACR-005",
|
"PACR-005",
|
||||||
|
"PACR-006",
|
||||||
"status: candidate",
|
"status: candidate",
|
||||||
"owner: jp",
|
"owner: jp",
|
||||||
]:
|
]:
|
||||||
@@ -577,6 +585,160 @@ def main() -> int:
|
|||||||
if remaining_gates.get(gate) != "blocked-follow-up":
|
if remaining_gates.get(gate) != "blocked-follow-up":
|
||||||
errors.append(f"memory_route_remaining_gate_missing:{gate}")
|
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", "<unknown>")
|
||||||
|
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"]:
|
for rel in ["AGENT.md", "CONTRACT.md", "DISCLOSURE.md", "README.md", "docs/STEEV-MASTER.md"]:
|
||||||
path = ROOT / rel
|
path = ROOT / rel
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
@@ -597,6 +759,7 @@ def main() -> int:
|
|||||||
"Personal-agent BlueBubbles binding",
|
"Personal-agent BlueBubbles binding",
|
||||||
"Proton/rclone package candidate",
|
"Proton/rclone package candidate",
|
||||||
"Personal-agent Secondbrain proposal route",
|
"Personal-agent Secondbrain proposal route",
|
||||||
|
"Personal-agent Conductor/Curator service handoff",
|
||||||
"superseded",
|
"superseded",
|
||||||
"legacy-reference",
|
"legacy-reference",
|
||||||
"blocked-follow-up",
|
"blocked-follow-up",
|
||||||
@@ -612,7 +775,7 @@ def main() -> int:
|
|||||||
|
|
||||||
result = {
|
result = {
|
||||||
"ok": not errors,
|
"ok": not errors,
|
||||||
"validator": "personal-agent-profile-distribution-v4",
|
"validator": "personal-agent-profile-distribution-v5",
|
||||||
"checked": REQUIRED,
|
"checked": REQUIRED,
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
|
|||||||
Reference in New Issue
Block a user