From 4c24af681525665fd90b0a903dcbd5709bf4887c Mon Sep 17 00:00:00 2001 From: Amy Hermes Date: Mon, 18 May 2026 08:32:21 -0400 Subject: [PATCH] docs: add outlook warning-draft sandbox contract --- HANDOFF.md | 7 +- README.md | 1 + ROADMAP.md | 38 ++++- ...microsoft-outlook-warning-draft-sandbox.md | 145 ++++++++++++++++++ ...microsoft_outlook_warning_draft_sandbox.py | 85 ++++++++++ 5 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 docs/microsoft-outlook-warning-draft-sandbox.md create mode 100644 tests/test_microsoft_outlook_warning_draft_sandbox.py diff --git a/HANDOFF.md b/HANDOFF.md index bcf5c52..5312fed 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -4,9 +4,9 @@ - GitHub: `https://github.com/Jdelg718/BotFucker` - Default branch: `main` -- Latest merged milestone: Phase 14 Durable Bridge Ledger Scaffold (`feat: add durable bridge ledger scaffold`, main at 8904d67) -- Current working branch: `phase-15-emergency-stop-bridge-rehearsal` -- Current PR target: Phase 15 Emergency-Stop Bridge Rehearsal — code/docs/tests only, dry-run-only, no OAuth/live provider mutation +- Latest merged milestone: Phase 15 Emergency-Stop Bridge Rehearsal (`feat: add emergency-stop bridge rehearsal`, main at 3b1075e) +- Current working branch: `phase-16-outlook-warning-draft-sandbox-contract` +- Current PR target: Phase 16 Microsoft Outlook Warning-Draft Sandbox Contract — docs/tests only, no OAuth/live provider mutation/no send - Selected next provider/action target: Microsoft Outlook warning draft in sandbox/manual reviewed bridge contract; no send-reply mutation yet - Current local demo target: demonstrate deterministic local review, optional mocked LLM classifier fallback/validation, approved-action export, dry-run n8n bridge contract, fail-closed YOLO policy checks, real n8n import validation results, the Phase 13 Reviewed Action Bridge Promotion Plan, durable bridge ledger dedupe scaffold, and emergency-stop bridge rehearsal - Current promo artifact: `promo/botfucker-animated-explainer/renders/botfucker-animated-explainer_narrated-final.mp4` @@ -38,6 +38,7 @@ docs/provider-auth-plan.md # provider auth/action boundary plan docs/reviewed-action-bridge-promotion-plan.md # Phase 13 reviewed live-bridge gate; no OAuth/live mutation docs/bridge-ledger-scaffold.md # Phase 14 durable processed-audit ledger scaffold; no OAuth/live mutation docs/bridge-rehearsal.md # Phase 15 emergency-stop/dry-run bridge rehearsal; no provider execution +docs/microsoft-outlook-warning-draft-sandbox.md # Phase 16 Outlook warning-draft sandbox contract; no send README.md # user-facing setup and project overview outreach_filter.py # compatibility CLI wrapper botfucker/models.py # normalized email/classification/review models diff --git a/README.md b/README.md index ede648b..371b7f5 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ See [DESIGN.md](DESIGN.md) for the proposed architecture and roadmap. - Provides an inactive n8n approved-action bridge starter that validates/dedupes actions in dry-run mode. - Provides a Phase 14 durable bridge ledger scaffold for recording processed `audit_id` state before provider mutation. - Provides a Phase 15 dry-run bridge rehearsal that proves emergency stop and duplicate-skip behavior before provider mutation. +- Documents the selected Phase 16 sandbox target: Microsoft Outlook warning-draft creation only, with no send-reply mutation. - Keeps provider credentials and live mailbox side effects outside the local UI and review queue. ## Safety First diff --git a/ROADMAP.md b/ROADMAP.md index ed21d47..2342d66 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -408,9 +408,33 @@ python3 -m botfucker.review_cli --db botfucker_review.sqlite3 import-webhook-jso Use fake or sanitized JSON only. Real mailbox payloads stay out of the repo. +### Phase 16 — Microsoft Outlook Warning-Draft Sandbox Contract ✅ + +Status: implemented on `phase-16-outlook-warning-draft-sandbox-contract`. + +Goal: document Kent's selected first provider/action target without adding OAuth, credentials, or live mutation behavior. + +Delivered: + +- `docs/microsoft-outlook-warning-draft-sandbox.md` +- Microsoft Graph `createReply` reference for draft creation +- explicit Outlook warning-draft-only target +- explicit `/send` and reply-send prohibition +- n8n/operator credential boundary +- acceptance criteria for a future implementation PR +- tests proving the contract exists and current n8n workflows still contain no Outlook send or credential material + +Acceptance criteria: + +- Provider target is Microsoft Outlook. +- First action is warning draft creation/save only. +- No send-reply mutation is allowed. +- Credentials stay outside BotFucker and inside n8n/operator infrastructure. +- Current workflow exports remain provider-call-free and credential-free. + ## Near-Term Recommendation -After Phase 15 is reviewed and merged, the next PR should be **Phase 16: Microsoft Outlook warning-draft sandbox contract**, not broad OAuth implementation. +After Phase 16 is reviewed and merged, the next PR should be **Phase 17: inactive Microsoft Outlook warning-draft workflow scaffold**, not broad OAuth implementation. Kent selected the first provider/action target: @@ -427,14 +451,14 @@ Recommended scope: - map the Phase 15 rehearsal outcomes onto an inactive n8n/operator checklist - require Rex/Gus security/ops review before any live mutation node is connected -OAuth can still wait. Phase 15 proves the brakes in code. Next is picking the sandbox road — not handing the robot live mailbox keys because apparently we enjoy learning by fire. +OAuth can still wait. Phase 16 picks the sandbox road. Next is building the inactive/manual Outlook draft scaffold — not handing the robot live mailbox keys because apparently we enjoy learning by fire. -### Restart checklist after Phase 15 +### Restart checklist after Phase 16 -1. Re-check Phase 15 PR CI and mergeability. -2. Squash-merge Phase 15 into `main` if still green. -3. Use Microsoft Outlook as the selected sandbox provider target. -4. Limit the first provider/action pair to warning draft creation/save only; do not send replies. +1. Re-check Phase 16 PR CI and mergeability. +2. Squash-merge Phase 16 into `main` if still green. +3. Build only an inactive/manual Microsoft Outlook warning-draft workflow scaffold. +4. Keep the provider action to create/save draft only; do not send replies. 5. Keep provider credentials inside n8n/operator infrastructure only; do not put secrets in BotFucker. ## Team Utilization diff --git a/docs/microsoft-outlook-warning-draft-sandbox.md b/docs/microsoft-outlook-warning-draft-sandbox.md new file mode 100644 index 0000000..ea3c815 --- /dev/null +++ b/docs/microsoft-outlook-warning-draft-sandbox.md @@ -0,0 +1,145 @@ +# Phase 16 — Microsoft Outlook Warning-Draft Sandbox Contract + +Kent selected the first provider/action target: + +- Provider: Microsoft Outlook / Microsoft Graph +- Action: create/save a warning draft only +- Environment: sandbox/manual reviewed bridge first +- Explicit non-goal: sending replies + +This phase is a contract and safety checklist for the future n8n/provider bridge. It does not add OAuth code, credentials, tokens, live n8n activation, or provider mutation logic to BotFucker core. + +## Microsoft Graph primitive + +Microsoft Graph supports creating a reply draft with: + +```http +POST /me/messages/{id}/createReply +``` + +or the user-scoped form: + +```http +POST /users/{id | userPrincipalName}/messages/{id}/createReply +``` + +Microsoft documents the least-privileged permission for this operation as `Mail.ReadWrite`. + +Important behavior: + +- `createReply` creates a draft reply. +- It does not send the draft. +- Sending is a separate operation and remains out of scope. +- The bridge must never call `/send` in this phase. + +Reference: + +- https://learn.microsoft.com/en-us/graph/api/message-createreply?view=graph-rest-1.0 + +## Required safety gates + +Before any sandbox call is allowed, the bridge must prove all of these gates: + +1. Human-approved local audit event exists. +2. Approved-action export schema is `botfucker.approved_actions.v1`. +3. `provider_execution` is `not_performed`. +4. Approved action is exactly `approve_warning`. +5. Provider is exactly `outlook` or `microsoft_outlook`, normalized to a single bridge value. +6. Durable bridge ledger claim succeeds for the `audit_id`. +7. Emergency stop is false. +8. Dry-run rehearsal has passed for the same action shape. +9. Sandbox mailbox/provider target is explicitly configured in n8n/operator infrastructure. +10. Credentials remain in n8n/operator infrastructure only. + +If any gate fails, the bridge exits before provider mutation. + +## Credential boundary + +Credentials are not BotFucker state. + +Allowed: + +- n8n-owned Microsoft credential +- operator-owned Microsoft sandbox account +- environment/credential store controlled outside this repo + +Forbidden in this repo: + +- OAuth client secret +- refresh token +- access token +- password +- cookie +- exported n8n credentials +- real mailbox payloads +- private message bodies + +## Allowed provider effect for first sandbox test + +The only permitted provider effect is: + +```text +create Outlook reply draft warning for the reviewed message +``` + +The provider result must be recorded back to the bridge ledger as metadata only, such as: + +- provider: `microsoft_outlook` +- approved_action: `approve_warning` +- provider_result_id: draft message ID or opaque test ID +- status: `processed` only after Graph returns a successful draft creation + +The ledger must still avoid storing raw message bodies, headers, secrets, or tokens. + +## Explicitly forbidden provider effects + +Do not: + +- send the draft +- call Microsoft Graph `/send` +- reply in a single operation +- delete email +- move email +- archive email +- mark read/unread +- create rules +- update contacts +- write mailbox settings +- attach credentials to exported workflow JSON + +No “just testing” send. That is how a sandbox turns into a customer-service fire drill wearing a fake mustache. + +## n8n sandbox shape + +A future n8n workflow may contain these conceptual steps: + +1. Manual trigger only. +2. Read approved-action export from controlled test input. +3. Validate schema/safety fields. +4. Check emergency-stop flag. +5. Claim `audit_id` in durable bridge ledger. +6. Use Microsoft Outlook/Graph credential from n8n only. +7. Create reply draft with Microsoft Graph `createReply`. +8. Update draft body with the human-approved warning text if the create step produces an empty draft. +9. Record provider draft ID in bridge ledger. +10. Stop. Do not send. + +The workflow must remain inactive/manual until Rex and Gus review the exact artifact. + +## Acceptance criteria for implementation PR + +A future implementation PR must include: + +- an inactive/manual n8n workflow or operator guide only +- sample fake Outlook action bundle using reserved/fake IDs +- tests/static validator proving no `/send` call exists +- tests/static validator proving no credentials are exported +- emergency-stop test evidence +- duplicate `audit_id` test evidence +- rollback/remediation note for deleting the sandbox draft manually +- Rex security review +- Gus ops/n8n review + +## Current status + +Phase 16 selected target is documented, but no live provider mutation has been implemented yet. diff --git a/tests/test_microsoft_outlook_warning_draft_sandbox.py b/tests/test_microsoft_outlook_warning_draft_sandbox.py new file mode 100644 index 0000000..5cda2bb --- /dev/null +++ b/tests/test_microsoft_outlook_warning_draft_sandbox.py @@ -0,0 +1,85 @@ +import json +import unittest +from pathlib import Path + +DOC_PATH = Path("docs/microsoft-outlook-warning-draft-sandbox.md") +WORKFLOW_PATHS = [ + Path("docs/n8n-workflow.json"), + Path("docs/n8n-approved-action-bridge.json"), +] + + +class MicrosoftOutlookWarningDraftSandboxContractTests(unittest.TestCase): + def test_contract_exists_and_targets_outlook_warning_draft_only(self): + text = DOC_PATH.read_text(encoding="utf-8") + + self.assertIn("Microsoft Outlook", text) + self.assertIn("warning draft", text.lower()) + self.assertIn("approve_warning", text) + self.assertIn("POST /me/messages/{id}/createReply", text) + self.assertIn("Mail.ReadWrite", text) + self.assertIn("Explicit non-goal: sending replies", text) + + def test_contract_forbids_send_and_other_provider_mutations(self): + text = DOC_PATH.read_text(encoding="utf-8") + + required_phrases = [ + "The bridge must never call `/send` in this phase.", + "Do not:", + "send the draft", + "call Microsoft Graph `/send`", + "delete email", + "move email", + "archive email", + "mark read/unread", + "No “just testing” send.", + ] + for phrase in required_phrases: + with self.subTest(phrase=phrase): + self.assertIn(phrase, text) + + def test_contract_keeps_credentials_out_of_repo(self): + text = DOC_PATH.read_text(encoding="utf-8") + + for forbidden_secret in [ + "OAuth client secret", + "refresh token", + "access token", + "password", + "cookie", + "exported n8n credentials", + ]: + with self.subTest(forbidden_secret=forbidden_secret): + self.assertIn(forbidden_secret, text) + self.assertIn("Credentials are not BotFucker state.", text) + self.assertIn("Credentials remain in n8n/operator infrastructure only.", text) + + def test_existing_n8n_workflows_still_do_not_contain_outlook_send_or_credentials(self): + forbidden_terms = [ + "/send", + "sendMail", + "microsoftGraph", + "client_secret", + "refresh_token", + "access_token", + "password", + ] + for path in WORKFLOW_PATHS: + raw = path.read_text(encoding="utf-8") + json.loads(raw) + lowered = raw.lower() + for term in forbidden_terms: + with self.subTest(path=str(path), term=term): + self.assertNotIn(term.lower(), lowered) + + def test_roadmap_records_kents_selected_phase_16_target(self): + text = Path("ROADMAP.md").read_text(encoding="utf-8") + + self.assertIn("Phase 16 — Microsoft Outlook Warning-Draft Sandbox Contract", text) + self.assertIn("Provider: Microsoft Outlook", text) + self.assertIn("First action: create/save a warning draft only", text) + self.assertIn("no send-reply mutation yet", text) + + +if __name__ == "__main__": + unittest.main()