From d4514e608d16844dec9c5211778d1eb2cae8bddb Mon Sep 17 00:00:00 2001 From: Chaim Date: Wed, 10 Jun 2026 09:28:19 +0000 Subject: [PATCH] =?UTF-8?q?feat(web):=20Agent=20Platform=20Port=20?= =?UTF-8?q?=E2=80=94=20Paperclip=20behind=20a=20single=20import=20seam=20(?= =?UTF-8?q?R2,=20G12,=20#111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit יוצר web/agent_platform_port.py כמודול היחיד שמייבא web.paperclip_client/paperclip_api. app.py מייבא כעת אך-ורק מה-Port — 0 imports ישירים של paperclip_* (היפוך-תלות פנימה, Ports & Adapters / Dependency Rule). החלפת-הפלטפורמה = מימוש-מחדש של מודול אחד. שתי שכבות ב-Port: (א) side-effects של מחזור-חיים נחשפים גם בשם-דומיין (archive_case_project/restore/create/notify_case_status — אירועי-הדומיין המומלצים לקוד חדש); (ב) פעולות issue/interaction/comment/agent — re-export בשם pc_* (קריאות API, לא אירועים). מעבר מלא לפעלי-דומיין = follow-up; ה-import seam (החלק הניתן-לאכיפה ב-G12) מוחזק בכל מקרה. שינוי import-only באתרי-הקריאה — אפס שינוי-התנהגות, regression מינימלי. אימות: app.py 0 imports ישירים; py_compile OK; כל 23 הסמלים נפתרים מה-Port; domain-aliases identity-wired; test_paperclip_access_guard 5 passed. Invariants: G12 (שער-הפלטפורמה — seam יחיד), G2 (מקור-אמת יחיד למגע-פלטפורמה). Co-Authored-By: Claude Opus 4.8 (1M context) --- web/agent_platform_port.py | 96 ++++++++++++++++++++++++++++++++++++++ web/app.py | 46 +++++++++--------- 2 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 web/agent_platform_port.py diff --git a/web/agent_platform_port.py b/web/agent_platform_port.py new file mode 100644 index 0000000..86a2ae8 --- /dev/null +++ b/web/agent_platform_port.py @@ -0,0 +1,96 @@ +"""Agent Platform Port (INV-G12 / docs/spec/X15) — the single seam to the platform. + +Paperclip is the *agent platform*: a replaceable shell, not the core. This module +is the ONLY place in the web layer that may import the Paperclip client +(``web.paperclip_client`` / ``web.paperclip_api``). ``web/app.py`` — and any future +web code — imports the platform operations from HERE, never from the Paperclip +modules directly. So the dependency points inward (Ports & Adapters / the +Dependency Rule, Cockburn / Martin): swapping the platform means re-implementing +this one module, not touching app.py. + +Enforced by the leak-guard (X15 §4 / R4): no file outside this Port imports +``paperclip_client`` / ``paperclip_api``. + +Two tiers: + • **Lifecycle side-effects** — archive / restore / create a case's project, and + the case-status notification — are also exposed under domain names + (``archive_case_project`` …). These are the genuine domain events the app + emits over a case's lifecycle; prefer them in new code. + • **Issue / interaction / comment / agent operations** are re-exported under + their existing ``pc_*`` names: they are request/response API calls, not + events, so a faithful facade re-export — rather than an artificial "event" — + is the honest treatment. Migrating these to domain verbs is a follow-up that + can happen as call sites are touched; the import seam (the enforceable part + of G12) holds regardless. +""" +from __future__ import annotations + +# ── the Paperclip shell — imported ONLY here (the Port is the single seam) ── +from web.paperclip_api import ( + emit_case_status_webhook, + pc_request, + require_paperclip_db_url, +) +from web.paperclip_client import ( + COMPANIES as PAPERCLIP_COMPANIES, + accept_interaction as pc_accept_interaction, + archive_project as pc_archive_project, + create_project as pc_create_project, + create_workflow_issue as pc_create_workflow_issue, + get_agents_for_case as pc_get_agents_for_case, + get_agents_for_company as pc_get_agents, + get_case_issues as pc_get_case_issues, + get_issue_comments as pc_get_issue_comments, + get_issue_interactions as pc_get_issue_interactions, + get_project_url, + post_comment as pc_post_comment, + reject_interaction as pc_reject_interaction, + respond_to_interaction as pc_respond_to_interaction, + restore_project as pc_restore_project, + wake_analyst_for_appraiser_facts as pc_wake_analyst_for_appraiser_facts, + wake_ceo_agent as pc_wake_ceo, + wake_ceo_for_feedback_fold as pc_wake_ceo_for_feedback_fold, + wake_curator_for_final as pc_wake_curator_for_final, + wake_for_precedent_extraction as pc_wake_for_precedent_extraction, +) + +# ── domain-named lifecycle aliases (preferred for new call sites) ─────────── +archive_case_project = pc_archive_project +restore_case_project = pc_restore_project +create_case_project = pc_create_project +notify_case_status = emit_case_status_webhook + +__all__ = [ + # platform infrastructure + "PAPERCLIP_COMPANIES", + "pc_request", + "require_paperclip_db_url", + "get_project_url", + # lifecycle — domain names (preferred) + legacy aliases + "archive_case_project", + "restore_case_project", + "create_case_project", + "notify_case_status", + "emit_case_status_webhook", + "pc_archive_project", + "pc_restore_project", + "pc_create_project", + # issues / workflow + "pc_create_workflow_issue", + "pc_get_case_issues", + # agents / wakeups + "pc_get_agents_for_case", + "pc_get_agents", + "pc_wake_ceo", + "pc_wake_ceo_for_feedback_fold", + "pc_wake_curator_for_final", + "pc_wake_for_precedent_extraction", + "pc_wake_analyst_for_appraiser_facts", + # comments / interactions + "pc_post_comment", + "pc_get_issue_comments", + "pc_get_issue_interactions", + "pc_accept_interaction", + "pc_reject_interaction", + "pc_respond_to_interaction", +] diff --git a/web/app.py b/web/app.py index 5acda59..5451f59 100644 --- a/web/app.py +++ b/web/app.py @@ -47,28 +47,32 @@ from web.mcp_env_catalog import ( normalize_for_compare, ) from web.progress_store import ProgressStore -from web.paperclip_api import emit_case_status_webhook, pc_request, require_paperclip_db_url -from web.paperclip_client import ( - COMPANIES as PAPERCLIP_COMPANIES, - accept_interaction as pc_accept_interaction, - archive_project as pc_archive_project, - create_project as pc_create_project, - create_workflow_issue as pc_create_workflow_issue, - get_agents_for_case as pc_get_agents_for_case, - get_agents_for_company as pc_get_agents, - get_case_issues as pc_get_case_issues, - get_issue_comments as pc_get_issue_comments, - get_issue_interactions as pc_get_issue_interactions, +# Agent platform access goes through the Platform Port ONLY (INV-G12 / X15) — +# app.py never imports web.paperclip_client / web.paperclip_api directly. +from web.agent_platform_port import ( + PAPERCLIP_COMPANIES, + emit_case_status_webhook, get_project_url, - post_comment as pc_post_comment, - reject_interaction as pc_reject_interaction, - respond_to_interaction as pc_respond_to_interaction, - restore_project as pc_restore_project, - wake_analyst_for_appraiser_facts as pc_wake_analyst_for_appraiser_facts, - wake_ceo_agent as pc_wake_ceo, - wake_ceo_for_feedback_fold as pc_wake_ceo_for_feedback_fold, - wake_curator_for_final as pc_wake_curator_for_final, - wake_for_precedent_extraction as pc_wake_for_precedent_extraction, + pc_accept_interaction, + pc_archive_project, + pc_create_project, + pc_create_workflow_issue, + pc_get_agents, + pc_get_agents_for_case, + pc_get_case_issues, + pc_get_issue_comments, + pc_get_issue_interactions, + pc_post_comment, + pc_reject_interaction, + pc_request, + pc_respond_to_interaction, + pc_restore_project, + pc_wake_analyst_for_appraiser_facts, + pc_wake_ceo, + pc_wake_ceo_for_feedback_fold, + pc_wake_curator_for_final, + pc_wake_for_precedent_extraction, + require_paperclip_db_url, ) -- 2.49.1