Skip to content

About

Observable AI workflow automation: Gmail polling, schema-driven processing, and Slack/Notion actions.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

22 Commits

Folders and files

Repository files navigation

StackBridge

An AI-assisted workflow automation project with deterministic workflow contracts, observable execution, and explicit external-side-effect semantics.

Backend migration verification Frontend verification

StackBridge executes a deliberately bounded v1 pipeline:

manual input or Gmail polling -> schema-driven AI processing -> Slack or Notion

It is an engineering portfolio project, not a hosted service. The repository emphasizes executable contracts, PostgreSQL-backed ownership and concurrency invariants, migration reproducibility, bounded recovery behavior, and honest handling of ambiguous provider writes.

StackBridge dashboard showing synthetic workflows, run outcomes, and estimated model cost

The current Dashboard rendering a fully synthetic local demo dataset.

Supported v1 contract

Surface Supported
Triggers Manual API/UI execution; Gmail polling
AI tasks Classify and summarize; extract fields; generate content
Models gpt-4o-mini; gpt-4o
Actions slack_post; notion_create_page
Integrations Gmail OAuth for polling; Slack OAuth and Notion OAuth for actions
Workflow shape Trigger -> AI step -> one action, including schema-driven branching

Scheduled triggers, webhooks, Gmail-send actions, workflow rollback, run replay, and automatic DLQ retry are not implemented.

Engineering highlights

  • Workflow YAML is parsed into a strict, versioned executable contract. Unsupported fields, providers, models, and trigger/action combinations fail closed.
  • Integration IDs are checked against the workflow owner when definitions are created, updated, activated, and executed.
  • Gmail message claims use PostgreSQL uniqueness to prevent duplicate run creation for the same supported idempotency key.
  • Refresh JWTs carry a jti bound to a persisted, single-use session. Rotation consumes the old session atomically before creating its replacement.
  • OAuth token values are encrypted with AES-256-GCM before database storage.
  • AI responses are validated against the workflow output schema. Semantic failures may receive bounded corrective attempts, with known token usage accumulated across billable attempts.
  • Slack and Notion writes use bounded, provider-specific retry rules. Ambiguous writes are reported as an unknown external outcome rather than blindly re-sent.
  • Terminal non-dry-run failures create an operator-visible DLQ entry. Resolving an entry acknowledges it; it does not replay the run.
  • Alembic migrations bootstrap an empty PostgreSQL database and include downgrade/destructive-operation guards exercised by tests.

Architecture

flowchart LR
    UI[React 19 / Vite 8] --> API[FastAPI REST API]
    API --> AUTH[Auth and refresh sessions]
    API --> WF[Workflow and version services]
    API --> EXEC[Run executor]
    API --> DLQ[DLQ and cost aggregation]
    SCHED[Embedded APScheduler<br/>SQLite job store] --> GMAIL[Gmail polling]
    GMAIL --> EXEC
    AUTH --> PG[(PostgreSQL 15)]
    WF --> PG
    EXEC --> PG
    DLQ --> PG
    EXEC --> OPENAI[OpenAI API]
    GMAIL --> GMAILAPI[Gmail API]
    EXEC --> SLACK[Slack API]
    EXEC --> NOTION[Notion API]
Loading

The supported topology is one backend process/instance with an embedded scheduler. PostgreSQL is authoritative application state; the SQLite job store persists scheduler jobs. The project does not implement distributed scheduler leadership or multi-node scheduler coordination.

An empty migration bootstrap creates 13 application tables plus PostgreSQL's alembic_version table.

Workflow example

This example uses synthetic UUIDs. Replace them with active integrations owned by the current user before saving or executing it.

name: Support email triage
description: Classify a new support email and post a summary to Slack
version: "1"
trigger:
  type: polling
  platform: gmail
  integration_id: 11111111-1111-4111-8111-111111111111
  interval_seconds: 300
  filter: "label:inbox"
ai_step:
  task_type: classify_and_summarize
  model: gpt-4o-mini
  output_schema:
    priority:
      type: string
      enum: [low, medium, high, urgent]
    summary:
      type: string
action:
  type: slack_post
  integration_id: 22222222-2222-4222-8222-222222222222
  config:
    channel: C0123456789
    template: "StackBridge alert [{priority}]: {summary}"

StackBridge workflow editor showing a supported synthetic Gmail-to-Slack workflow

The current workflow editor validating a supported synthetic Gmail polling → gpt-4o-mini → Slack definition.

The editor also rejects incomplete definitions before they are persisted:

StackBridge workflow editor showing a validation error for an incomplete synthetic definition

Fail-closed contract validation in the current editor, using synthetic local input.

Workflow edits create new version rows. Historical YAML can be inspected and copied back into the editor, but there is no automatic rollback operation.

Reliability and failure semantics

Gmail polling uses the Gmail History API cursor. If a cursor becomes stale, recovery scans an explicit time window with server-controlled page, candidate-message, and matched-message budgets. It is bounded recovery, not arbitrary mailbox reconstruction.

The Gmail idempotency invariant prevents duplicate workflow-run creation for the same Gmail message key under the supported polling flow. It does not imply exactly-once provider delivery.

Provider failures are classified as either:

  • Definite failure: the provider rejected the request or a retryable response exhausted its bounded budget.
  • Unknown external outcome: a transport failure, server response, or unreadable success response leaves open the possibility that the provider write completed.

StackBridge Run Inspector showing a successful synthetic three-step run

Run Inspector rendering a synthetic local success with step statuses, recorded tokens, model, and estimated model cost.

Unknown outcomes require operator investigation. StackBridge does not automatically retry or replay them.

StackBridge Run Inspector showing a synthetic unknown external provider outcome

Run Inspector rendering a synthetic local demo run whose Notion write outcome is unknown; no external write was performed for this fixture.

Cost and dry-run semantics

Run and step cost fields are estimated model cost derived from recorded prompt/completion tokens and the price table for the supported model allowlist. They are not an invoice or authoritative provider-account spend. The cost summary reports all-time recorded tokens and estimated model cost, a per-workflow breakdown, and a recent 14-day series.

StackBridge Cost Dashboard showing synthetic recorded tokens and estimated model costs

The current Cost Dashboard aggregating coherent synthetic local run data; values are estimates, not provider billing.

Manual execution can request dry-run mode. A dry run still executes the AI step and can incur real model usage. It resolves the selected action and builds a provider-specific preview without fetching a provider token or performing the Slack/Notion write.

StackBridge Run Inspector showing a synthetic Slack dry-run preview

Run Inspector rendering a schema-correct synthetic Slack preview with Dry Run: Yes; no provider write occurred.

Security and reliability design

  • Argon2 password hashing.
  • Signed JWT access tokens and persisted one-time refresh-session rotation.
  • AES-256-GCM encryption before OAuth tokens are stored in PostgreSQL.
  • Owner-scoped workflow, integration, run, cost-summary, and DLQ queries; foreign resources are hidden where the API exposes object lookup.
  • OAuth state validation with a ten-minute, single-use in-process state record.
  • Bounded AI/provider retry budgets and explicit unknown-outcome handling.
  • Minimum read-only GitHub Actions permissions and no real provider credentials in CI.

See SECURITY.md for private vulnerability reporting guidance.

Tech stack

Layer Technology
Frontend React 19, Vite 8, React Router 7, Recharts, JavaScript
Backend Python 3.11.9, FastAPI, Pydantic v2, SQLAlchemy 2 async
Data PostgreSQL 15, Alembic; SQLite APScheduler job store
AI OpenAI Chat Completions over httpx
Auth Argon2, python-jose, access/refresh JWTs, persisted refresh sessions
Providers Gmail, Slack, and Notion OAuth/API integrations
CI GitHub Actions on Python 3.11.9 and Node.js 24

Local setup

Prerequisites

  • Git
  • Python 3.11
  • Node.js 24 with npm
  • Docker with Compose

1. Clone and start PostgreSQL

git clone https://github.com/BadrDyane/stackbridge.git
cd stackbridge/backend
docker compose up -d postgres

If your Docker installation exposes the standalone command, use docker-compose up -d postgres instead.

2. Create the Python environment

PowerShell:

py -3.11 -m venv venv
.\venv\Scripts\Activate.ps1
python -m pip install -r requirements-dev.txt
Copy-Item .env.example .env

macOS/Linux:

python3.11 -m venv venv
source venv/bin/activate
python -m pip install -r requirements-dev.txt
cp .env.example .env

Generate an application signing secret and a 64-character hexadecimal encryption key:

python -c "import secrets; print('APP_SECRET_KEY=' + secrets.token_urlsafe(48)); print('ENCRYPTION_KEY=' + secrets.token_hex(32))"

Copy those two generated values into backend/.env. The committed example contains deliberately unusable secret placeholders.

The database, migrations, tests, frontend build, and core API startup do not require live provider calls. Actual OpenAI execution and Gmail/Slack/Notion OAuth or API operations require valid provider credentials in .env.

3. Migrate and start the API

From backend/ with the virtual environment active:

alembic upgrade head
uvicorn app.main:app --reload --port 8000

The reload flag is for local development only.

4. Install and start the frontend

In another terminal:

cd stackbridge/frontend
npm ci
npm run dev

Open http://localhost:5173.

5. Verify locally

Backend (from backend/, with PostgreSQL running):

python -m pytest -c pytest.ini tests

Frontend (from frontend/):

npm test
npm run lint
npm run build

API surface

Swagger/OpenAPI is the canonical complete reference. The implemented routes are:

Method Route Purpose
GET /health API/database health
POST /auth/register Register an account
POST /auth/login Issue an access/refresh token pair
POST /auth/refresh Atomically rotate a refresh session
GET /auth/me Read the current user
GET, POST /workflows List or create workflows
GET /workflows/{workflow_id} Read an owned workflow
GET, POST /workflows/{workflow_id}/versions List or create workflow versions
POST /workflows/validate Validate YAML without saving
PATCH /workflows/{workflow_id}/trigger-config Upsert Gmail polling configuration
PATCH /workflows/{workflow_id}/action-config Upsert the action override
POST /workflows/{workflow_id}/activate Validate and activate a workflow
POST /workflows/{workflow_id}/deactivate Deactivate a workflow
POST /runs/trigger Execute a manual run, optionally dry-run
GET /runs List owned runs
GET /runs/cost-summary Read owner-scoped estimated model costs
GET /runs/{run_id} Inspect an owned run and its steps
GET /integrations/{platform}/auth-url Begin an authenticated OAuth flow
GET /integrations/{platform}/callback Complete provider OAuth
GET /integrations List active integrations
POST /integrations/{integration_id}/test Test an owned integration
DELETE /integrations/{integration_id} Disconnect an owned integration
GET /dlq List unresolved owned DLQ entries
POST /dlq/{dlq_id}/resolve Acknowledge an owned DLQ entry

Verification evidence

At the Phase 3F checkpoint, the repository passed:

  • 167 PostgreSQL-backed backend tests on Python 3.11.9, including full migration bootstrap/downgrade verification.
  • 26 frontend tests on Node.js 24, plus repository lint and a production build.
  • A clean-clone exercise using a fresh Python environment, npm ci, an empty PostgreSQL database upgraded to Alembic head, API startup, and a healthy database probe.

Both GitHub Actions workflows run the full relevant suites; neither workflow uses real provider secrets.

Current scope and limitations

  • The scheduler topology is one backend process/instance; there is no distributed leader election.
  • Only manual and Gmail-polling triggers, and Slack/Notion actions, are executable v1 contracts.
  • There is no replay engine or automatic DLQ retry.
  • Unknown provider outcomes require operator investigation and may represent a completed external write.
  • Stale Gmail cursor recovery is bounded and may leave a gap before its configured recovery window.
  • Browser access and refresh tokens are stored in localStorage. Refresh recovery is single-flight within one page, not coordinated across tabs.
  • Client logout clears browser state but does not proactively revoke the current server-side refresh session.
  • OAuth state is stored in the backend process, so an in-progress OAuth flow does not survive a process restart or transfer to another instance.

Project structure

stackbridge/
|-- .github/workflows/          # Backend migration and frontend verification
|-- backend/
|   |-- alembic/                # Five migrations through the current head
|   |-- app/
|   |   |-- api/                # Auth, workflow, run, integration, and DLQ routes
|   |   |-- contracts.py        # Executable v1 allowlists and model pricing
|   |   |-- engine/             # Normalize -> AI -> action execution
|   |   |-- integrations/       # Gmail trigger; Slack/Notion actions; OAuth
|   |   |-- models/             # 13 application tables, including refresh sessions
|   |   |-- scheduler/          # Gmail polling and reconciliation
|   |   `-- services/           # Workflow, DLQ, integration, and LLM services
|   `-- tests/                  # PostgreSQL-backed backend verification
|-- frontend/
|   |-- src/api/session.js      # Token-pair persistence and legacy migration
|   |-- src/utils/runOutcome.js # Safe external-outcome presentation
|   `-- tests/                  # Session/client and run-outcome tests
`-- screenshots/                # Sanitized, current public product evidence

Author

Built by Badr Dyane.

License

StackBridge is licensed under the MIT License.

About

Observable AI workflow automation: Gmail polling, schema-driven processing, and Slack/Notion actions.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages