Arbor Β· System Diagrams

6 architecture diagrams Β· grounded in schema.sql, sops.json, skills, & match-review.html

What are these diagrams? (30-second read)

They're flowcharts β€” visual maps of how our advisory machine actually runs, step by step. Drawn with a tool called Mermaid, but the tool doesn't matter β€” what matters is the picture: boxes are steps, arrows are what moves things forward, diamonds are yes/no decisions, and notes off to the side flag who owns a step or what's not built yet.

1Client / Founder Lifecycle

Prospect β†’ Free Pack β†’ Signing β†’ 6-Week Cadence β†’ 3-Week Refresh β†’ Ongoing

The full founder journey from inbound triage through the free-pack trigger, kickoff, the six-week delivery cycle, and the three-week context refresh that keeps profiles current.

flowchart TD IB["Inbound Lead
Founder reaches out"] TRIAGE["Inbound Triage
sop-inbound-triage
4-tier classification"] IB --> TRIAGE TRIAGE -->|"Tier A: pre-rev SaaS"| GOOD["Good Fit
Schedule intro call"] TRIAGE -->|"Tier B: pre-rev high-capex"| MAYBE["Conditional Fit
Assess further"] TRIAGE -->|"Tier C/D: post-rev / advice-only"| PASS["Pass / Redirect
Scripted response"] GOOD --> INTRO_CALL["Intro Call
1st transcript
Trigger: founder-profiler
+ initial meeting-ingestor run"] MAYBE --> INTRO_CALL INTRO_CALL -->|"profile created"| FP4["founder-profiler run
founders.json updated
assets/profiles.json"] FP4 --> FREE["Free Pack Delivery
2 LinkedIn posts
1 IR newsletter draft
no investor intros yet"] FREE --> DECISION{"Founder Signs?"} DECISION -->|"No"| NURTURE["Nurture / Archive
profile retained"] DECISION -->|"Yes"| KICKOFF_CALL["Kickoff Call
2nd transcript
Trigger: full system kickoff"] KICKOFF_CALL --> ONBOARD["Onboard
Set ICPs + investor prefs
Matching run
Context established"] ONBOARD --> WK1["Week 1-2
2 LI posts published
Matching underway
Context refresh if needed"] WK1 --> WK3["Week 3
Context Refresh
Check-in call or async
Profile updates if needed"] WK3 --> WK4["Week 4-5
2 LI posts/wk continues
1 investor intro ~wk 4
IR newsletter drafted"] WK4 --> WK5["Week 5-6
IR newsletter finalized
+ Canva PDF
Distributed to investor network"] WK5 --> WK6_END["End of Cycle 1
Weekly check-in
Cycle review"] WK6_END --> REFRESH["Every 3 Weeks
Context-Refresh Interview
TFG-driven 15-30 min recorded"] REFRESH --> NEXT_CYCLE["Next 6-Week Cycle
6 intros Β· 12 LI posts Β· 1 newsletter"] NEXT_CYCLE --> REFRESH style TRIAGE fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style FREE fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style KICKOFF_CALL fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style NEXT_CYCLE fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style REFRESH fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style PASS fill:#201015,stroke:#8B92A0,color:#E8E6E0
Free pack spec (2 LI + 1 IR newsletter) from the task brief β€” not explicitly in sops.json but consistent with sop-six-week-cycle. Kickoff as 2nd transcript and 1st transcript as intro call from task brief and sop-meeting-ingestor. 3-week refresh cadence from task brief; the six-week cycle contains two such refreshes internally.
2End-to-End System Data-Flow

Sources β†’ Ingest / Profile β†’ Match β†’ Content Skills β†’ Review Pages β†’ Deliverables

Shows where data originates, how it flows through profiling and matching, how content is generated, and how the D1 app store and human "faucet" gate every outbound action.

flowchart TD subgraph SOURCES["Source Layer"] TR["Client Transcripts
Client meeting Transcripts/"] FJ["founders.json
source-of-truth profiles"] IJ["investors.json
source-of-truth profiles"] GD["Google Drive
Deck Β· PDF Β· Meeting notes"] DB_AEP["AEP Prod
Postgres read-only
(Alex β€” backlog)"] end subgraph INGEST["Ingest + Profile Layer (Tim's Machine β€” Faucet)"] MI["meeting-ingestor skill
14-step extraction
JSON + Markdown output"] FP["founder-profiler skill
5-dim profile
founders.json"] IP["investor-profiler skill
3-dim profile
investors.json"] end subgraph MATCH["Match Layer (Tim's Machine β€” Faucet)"] FM["founder-matcher skill
Additive scoring Β· no hard gates
matches.json direction=investors_for_founder"] IM["investor-matcher skill
Additive scoring Β· no hard gates
matches.json direction=founders_for_investor"] MJ["assets/matches.json
ranked pairs Β· flags Β· scores"] end subgraph CONTENT["Content-Generation Skills (Tim's Machine β€” Faucet)"] LI["linkedin-post-writer skill
3 posts per run Β· founder voice
Capital-One framing"] IRN["ir-newsletter-writer skill
offering_breakdown β†’ Β§1 Β§2 Β§3
350-550 words"] IID["investor-intro-drafter skill
Ben-voiced Β· 3-stage package
loads ben-voice skill"] BV["ben-voice skill
voice corpus Β· quote bank
loaded by IID"] end subgraph STATIC["Static Review Layer (Cloudflare Pages + Access)"] MR["match-review.html
3-button feedback UI
founder Γ— investor pairs"] SL["investor-library.html
founder-library.html"] MB["investor-match-board.html"] end subgraph D1["D1 App Store (Cloudflare D1)"] MF_T["match_feedback
verdict + reviewer + ts"] REQ["requests
status: queued→copy_edit
→final→approved"] APR["approvals
stage: copy_edit | final"] MRK["marks
kanban stage Β· stamps"] AUD["audit
immutable log"] USR["app_users
role cache"] end subgraph HUMANS["Human Review Gate"] COPY["Copy-Edit
Refael (content)
Saviour / Cullen"] FINAL["Final Approval
Ben"] end subgraph DELIVER["Deliverables"] LI_OUT["LinkedIn Posts
Google Doc β†’ Founder"] IRN_OUT["IR Newsletter
Canva PDF β†’ Investor Network"] INTRO_OUT["3-Stage Intro Package
Google Drive β†’ Ben sends"] end TR -->|"transcript"| MI GD -->|"deck / notes"| FP GD -->|"meeting notes"| IP DB_AEP -.->|"future: metrics"| FM DB_AEP -.->|"future: metrics"| IM MI -->|"outputs_meetings JSON"| FP MI -->|"offering_breakdown"| IRN FJ --> FM FJ --> IM IJ --> FM IJ --> IM FP -->|"updates"| FJ IP -->|"updates"| IJ FM --> MJ IM --> MJ MJ -->|"pairs + scores"| MR MJ -->|"ranked"| MB FJ --> SL IJ --> SL MR -->|"POST /api/feedback"| MF_T MR -->|"POST /api/requests
(good verdict)"| REQ REQ -->|"queued"| IID IID -->|"loads"| BV IID -->|"draft"| COPY MI -->|"transcript + profile"| LI MI -->|"offering_breakdown"| IRN COPY -->|"approved"| FINAL COPY -->|"changes_requested"| APR FINAL -->|"approved"| APR APR -->|"status=approved"| REQ FINAL -->|"approved"| LI_OUT FINAL -->|"approved"| IRN_OUT FINAL -->|"approved"| INTRO_OUT REQ --> MRK MF_T --> AUD REQ --> AUD APR --> AUD USR -.->|"role check"| COPY USR -.->|"role check"| FINAL style SOURCES fill:#1E222A,stroke:#343A44,color:#E8E6E0 style INGEST fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style MATCH fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style CONTENT fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style STATIC fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style D1 fill:#1C1A26,stroke:#6B5EA8,color:#E8E6E0 style HUMANS fill:#1A2012,stroke:#2E6E5E,color:#E8E6E0 style DELIVER fill:#201A0A,stroke:#B5651D,color:#E8E6E0
Inferred: AEP Prod path is backlogged (ROADMAP.md obj 9). "Tim's machine β€” faucet" label is from ARCHITECTURE doc rule 4: agents run only on Tim's approval, humans drain. Static review pages are behind Cloudflare Access per ARCHITECTURE phase 1.
3Content-Production Pipeline (Per Founder)

Transcript / Profile β†’ LI / IR-Newsletter / Intro Skills β†’ Review β†’ Deliver

Three parallel content tracks seeded from the same cycle interview. All three converge at the two-gate review before delivery. The IR newsletter feeds LinkedIn themes each cycle (per sop-ir-newsletter).

flowchart TD INT["Cycle Interview
TFG-driven 15-30 min recorded
sop-six-week-cycle step 1"] TR["Transcript
Client meeting Transcripts/"] MI2["meeting-ingestor
14-step pipeline
JSON schema v1"] INT --> TR --> MI2 MI2 --> JS["outputs_meetings JSON
exec_summary Β· action_items
promises Β· offering_breakdown"] JS --> OB["offering_breakdown object
fresh_wins Β· needs_besides_capital
current_raise Β· linkedin_guidance
investor_intro_feedback + 4 more"] OB -->|"fresh_wins β†’ Β§1"| IRN2["ir-newsletter-writer
Β§1 Good News
Β§2 Asks
Β§3 Fundraise
350-550 words Β· founder voice"] OB -->|"needs_besides_capital β†’ Β§2"| IRN2 OB -->|"current_raise β†’ Β§3"| IRN2 OB -->|"linkedin_guidance"| LI2["linkedin-post-writer
3 posts per run Β· founder voice
Capital-One framing
>75% people focus"] MI2 -->|"voice from transcript"| LI2 MI2 -->|"voice profile"| LI2 MJ2["matches.json
approved founderΓ—investor pair"] MJ2 -->|"pair + rationale"| IID2["investor-intro-drafter
+ ben-voice corpus
Stage 1: SMS permission text
Stage 2: investor-only email
Stage 3: 3-way intro email"] IRN2 --> IRND["IR Newsletter Draft
Google Doc"] LI2 --> LID["LinkedIn Post Drafts
3 posts Β· Google Doc"] IID2 --> INTROD["Intro Package Draft
All 3 stages
Google Drive staged doc"] IRND --> CE2["Copy-Edit Gate
Refael / Saviour / Cullen"] LID --> CE2 INTROD --> CE2 CE2 -->|"approved"| FA2["Final Approval
Ben Wiggins"] CE2 -->|"changes"| IRND CE2 -->|"changes"| LID CE2 -->|"changes"| INTROD FA2 -->|"approved"| IRN_DEL["IR Newsletter
Canva PDF
β†’ Investor Network
~week 5 of cycle"] FA2 -->|"approved"| LI_DEL["LinkedIn Posts
2Γ—/week Mon-Thu
β†’ Founder publishes"] FA2 -->|"approved"| INT_DEL["Intro Package
Ben sends Stage 1 β†’ 2 β†’ 3
Refael weekly follow-up"] IRN_DEL -.->|"themes feed"| LI2 style INT fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style MI2 fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style JS fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style OB fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style IRN2 fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style LI2 fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style IID2 fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style CE2 fill:#1A2012,stroke:#2E6E5E,color:#E8E6E0 style FA2 fill:#1A2012,stroke:#2E6E5E,color:#E8E6E0
IR newsletter at ~week 5 is from sop-six-week-cycle. Canva PDF step from sop-ir-newsletter step 8. Voice profile capture from ROADMAP.md obj 5 addendum. IRN feeds LI themes confirmed in sop-ir-newsletter note field.
4Two-Gate Review and Approval Pipeline

Draft β†’ Copy-Edit β†’ Final β†’ Approved; feedback β†’ intro queue; idle escalation (TODO)

Every draft flows through two named gates before delivery. Match feedback from the review UI either queues an intro request or rotates investor assignments. D1 tables record every state transition immutably.

stateDiagram-v2 [*] --> Drafted : Skill generates draft (LI / IRN / Intro) Drafted --> CopyEdit : Actor queues request β€” status=queued, D1 requests row created state CopyEdit { direction LR [*] --> CopyReview : Refael or Saviour/Cullen opens review page CopyReview --> CopyApproved : decision=approved, D1 approvals row (copy_edit) CopyReview --> CopyChanges : decision=changes, D1 approvals row CopyChanges --> [*] : Draft revised and re-queued } CopyEdit --> FinalReview : status=final after copy approval state FinalReview { direction LR [*] --> BenReviews : Ben Wiggins, final_approver role BenReviews --> BenApproved : decision=approved, D1 approvals row (final) BenReviews --> BenChanges : decision=changes BenChanges --> [*] : Returns to copy-edit } FinalReview --> Approved : status=approved, D1 requests updated Approved --> Delivered : Human drains faucet (Ben sends intro or Refael posts LI) Delivered --> [*]
Roles (from _lib.js): copy-edit = Refael & Saviour (Saviour leads LinkedIn/marketing); final approval = Ben (final_approver); editors = Cullen, Adam, Abhijay. Idle-escalation (3-day rule, Cullen owns) is flagged in the SOPs but not yet wired to the app store.
5Investor Lifecycle

Sourced β†’ Profiled β†’ Matched β†’ QC Gate β†’ Ben Feedback β†’ 3-Stage Intro β†’ Response β†’ Feedback Loop

Tracks an investor from discovery through profiling, the QC must-not gate (Cullen), Ben's three-button verdict, the full three-stage intro sequence, and how responses feed back into the matching engine.

flowchart TD SRC["Investor Sourced
Ben network Β· team referral
founders supply own lists"] PROFILED["investor-profiler run
3 dims: Thesis Β· Advisory fit Β· Intangibles
Mandatory gap flagging
Gap β†’ investors.json marked"] WARM_CHECK{"Warm contact?
(founder's own list)"} COLD_PATH["Cold Match Path
sop-intro-sequence"] WARM_PATH["Warm Follow-Up Path
sop-warm-followup
CRM flag required first"] SRC --> PROFILED PROFILED --> MATCH_RUN["Matching Run
Additive scoring Β· no hard gates
Red-lines = dismissable flags only
matches.json updated"] MATCH_RUN --> QC["QC Gate
Cullen Martin β€” gate owner
sop-rec-qc
Must-not check before any intro"] QC -->|"fails must-not"| ROTATE["Rotate Investor
Next ranked match surfaced
D1 marks updated"] QC -->|"passes"| MR_UI["Match Review UI
match-review.html
Ben reviews pair"] MR_UI -->|"Ben votes"| VERDICT{"Ben Verdict
3 buttons"} VERDICT -->|"Good match
queues intro"| QUEUE["D1 requests row
status = queued
workflow = investor-intro-drafter"] VERDICT -->|"Worth a shot
saves only"| SAVED["D1 match_feedback
verdict = worth_a_shot
No intro queued yet"] VERDICT -->|"Not a good fit
rotates"| ROTATE QUEUE --> IID3["investor-intro-drafter skill
Ben-voiced 3-stage package
Google Drive staged doc"] IID3 --> CE3["Copy-Edit Gate
Refael / Cullen"] CE3 --> FA3["Final Gate
Ben Wiggins"] FA3 -->|"status = approved"| STAGE1["Stage 1 β€” SMS Permission
Hey first-name I have one
you'll like β€” may I send details?"] WARM_CHECK -->|"Yes"| WARM_PATH WARM_CHECK -->|"No"| COLD_PATH COLD_PATH --> STAGE1 STAGE1 -->|"investor replies yes"| STAGE2["Stage 2 β€” Investor-Only Email
Rationale Β· fit Β· TFG advisory value
Refael drafts Β· Ben / BWI sends"] STAGE2 -->|"investor interested"| STAGE3["Stage 3 β€” 3-Way Intro Email
Ben-voiced Β· SHORT/WARM default
cc benwigginsinvestor@
Signed 'Ben Wiggins Investment Team'"] STAGE3 --> WEEKLY["Weekly Follow-Up Thread
Refael owns
until investor responds
private note to founder"] WEEKLY --> RESPONSE{"Investor Response?"} RESPONSE -->|"Interested"| MEETING["Investor x Founder Meeting
Pitch call
Team may attend case-by-case
sop-pitch-attendance"] RESPONSE -->|"No response / pass"| ARCHIVE["Archive intro thread
BWI Zero Inbox rule
feedback logged"] RESPONSE -->|"Feedback on format/fit"| FB_LOOP["Feedback Loop
investor_intro_feedback bucket
updates investor profile
matching weights refined"] MEETING --> FB_LOOP ARCHIVE --> FB_LOOP FB_LOOP --> PROFILED style PROFILED fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style QC fill:#201015,stroke:#8B92A0,color:#E8E6E0 style MR_UI fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style QUEUE fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style IID3 fill:#1F1A0A,stroke:#B5651D,color:#E8E6E0 style STAGE1 fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style STAGE2 fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style STAGE3 fill:#15252A,stroke:#2E6E5E,color:#E8E6E0 style FB_LOOP fill:#1A1F2A,stroke:#46546F,color:#E8E6E0 style ROTATE fill:#201015,stroke:#8B92A0,color:#E8E6E0
Stage 1 SMS text verbatim from investor-intro-drafter/references/intro-flow.md. "Worth a shot saves only / no intro queued" inferred from match-review.html comment "saves only" vs good which calls /api/requests. QC gate owner Cullen from sop-rec-qc. Warm-contact suppression from sop-warm-followup.
6App-Store Data Model (D1 / SQLite)

Five D1 tables + role cache β€” from db/schema.sql verbatim

The Cloudflare D1 app store is the only team-writable datastore. Source-of-truth JSON files (founders.json, investors.json, matches.json) are never written by the web tier β€” only by Tim's local faucet agents.

erDiagram match_feedback { INTEGER id PK TEXT founder TEXT investor TEXT verdict "good | worth_a_shot | not_a_fit" TEXT context TEXT reviewer_email FK TEXT ts } requests { INTEGER id PK TEXT ts TEXT actor_email FK TEXT workflow "investor-intro-drafter etc" TEXT founder TEXT investor TEXT status "queued | copy_edit | final | approved | dismissed" TEXT notes } approvals { INTEGER id PK INTEGER request_id FK TEXT stage "copy_edit | final" TEXT decision "approved | changes" TEXT actor_email FK TEXT note TEXT ts } marks { INTEGER id PK TEXT entity_kind TEXT entity_id TEXT key TEXT value TEXT actor_email FK TEXT updated_at } audit { INTEGER id PK TEXT ts TEXT actor_email FK TEXT action TEXT detail } app_users { TEXT email PK TEXT role "owner | final_approver | copy_editor | editor | viewer" TEXT display_name } requests ||--o{ approvals : "has review gates" app_users ||--o{ match_feedback : "reviewer_email" app_users ||--o{ requests : "actor_email" app_users ||--o{ approvals : "actor_email" app_users ||--o{ marks : "actor_email" app_users ||--o{ audit : "actor_email" requests ||--o{ audit : "action logged" match_feedback ||--o{ audit : "action logged"
Schema is read verbatim from db/schema.sql. Seed data: Tim (owner), Ben (final_approver), Refael (copy_editor), Adam/Cullen/Abhijay (editor). The audit β†’ requests and audit β†’ match_feedback FK relationships are logical (audit.detail references IDs) β€” not enforced by FK constraint in SQLite. marks UNIQUE(entity_kind, entity_id, key) enforces one value per slot.
All diagrams grounded in: db/schema.sql Β· sops/sops.json Β· skills/*/SKILL.md Β· skills/investor-intro-drafter/references/intro-flow.md Β· skills/meeting-ingestor/references/schema.md Β· outputs_matches/match-review.html Β· ARCHITECTURE_secure-team-site.md Β· ROADMAP.md Β· functions/_lib.js Β· _build_site.py Β· Generated 2026-06-18