Skip to main content
Prototype
  • Full-Stack
  • Automation
  • AI and RAG

SocialAgent

A personal full-stack project: a FastAPI and React application that learns a writing style from imported posts, drafts platform-ready posts and replies from it, runs every draft through a guardrail battery, and moves nothing to a queue without an explicit approval. Approved content is scheduled and published exactly once by Celery workers through the platforms’ own OAuth APIs; incoming comments are ingested, threaded and triaged into the same review queue. The create → approve → publish → engage loop is complete and tested end to end against a fake platform adapter — no live account has been connected yet.

role
Personal project
period
August 2026
status
Prototype
View Case StudyPrivate Repository

01Overview

SocialAgent drafts social posts in a person’s own voice, schedules them, and drafts replies to the comments that come back — and puts a human decision in front of every one of them.

The approval step is not a setting. A draft moves `draft → approved → queued → published`, and the transition into a queue is the only path to publication, enforced in the data model and asserted by tests. There is no switch that turns it off.

It is code-complete against the MVP scope: voice profiles, drafting, guardrails, approval, scheduling, publishing, comment ingestion, reply drafting, a content calendar, evaluation metrics and a React front end for all of it. What has not happened is a live platform connection — every integration is verified against a fake adapter while the platform app reviews are outstanding, which is why this sits as a prototype rather than something shipped.

02Context

Tools that post automatically on someone’s behalf tend to produce content that reads like nobody wrote it, because nobody did. The failure is not occasional — an off-voice post is the normal output, and the person finds out when it is already public.

So the design question was never “how much can be automated?” but “where does the human decision sit, and what stops it being skipped?”. Putting that gate in the schema rather than in a policy is the decision the rest of the system is built around.

03Problem

Content work is repetitive enough to want help with and personal enough that fully automating it defeats the point — and the systems that do it touch someone’s public identity, their platform accounts and their credentials.

  • Drafts should sound like the person, which means learning their writing style rather than applying a generic voice.
  • Nothing reaches an audience without an explicit human approval, and that must be structural rather than a setting.
  • A generated draft can be off-brand, unsafe, or carry an instruction smuggled in through a comment — every draft has to be checked before a person is asked to approve it.
  • Platforms are reached only through their documented OAuth and publishing APIs, within the automation each one actually permits.
  • Platform tokens are credentials: encrypted at rest, never logged, never returned by the API.
  • A scheduled post must publish once — not zero times because a worker died, and not twice because it retried.
  • Incoming comments need triage before they need replies.

04My Role

My Role

This is my own project, built end to end: the planning set that preceded the code, the data model and migrations, authentication and the encrypted token vault, the OAuth integrations, voice extraction and draft generation behind a provider abstraction, the guardrail battery, the approval gate, the scheduling and publishing workers, comment ingestion and reply drafting, the evaluation harness and metrics, the React front end, and the CI and quality gates.

05Responsibilities

Voice and generation

  • Voice profiles: capturing sample posts and extracting a style guide from them.
  • Draft generation shaped by that style guide and by each platform’s own rules.
  • Reply drafting in the same voice, from a comment and its thread.
  • A provider abstraction so a local model in development and a hosted one in production are a configuration change.

Safety and approval

  • A guardrail battery over every draft: deterministic checks, model-based checks, and a prompt-injection scan for text that arrived from strangers.
  • The approval gate, enforced in the data model as the only path from draft to queue.
  • An approval inbox for posts and replies, with an audit trail behind every decision.

Publishing and engagement

  • Scheduling and a Celery beat publisher that publishes an approved post exactly once.
  • Retries, reconciliation and catch-up, so a missed slot is recovered or flagged rather than lost.
  • A per-platform rate governor.
  • Comment ingestion — polled and by webhook — deduplicated, threaded and triaged.
  • A content calendar over drafted, scheduled and published items.

Platform and quality

  • PostgreSQL schema and Alembic migrations; Docker Compose for the API, worker, database and queue.
  • A React and TypeScript front end: composer, approval inbox, calendar and settings, with its own design tokens and dark mode.
  • An offline golden set with a regression gate, and production metrics — edit rate per voice profile, safety pass rate, injection attempts.
  • CI gates: Ruff, strict mypy, detect-secrets, pytest, and security-invariant tests that assert every route is authenticated.

06Technical Approach

Build the loop in the order it runs, and make the human gate a property of the data model rather than a step the code is trusted to call.

  1. 01Learn a voice: import sample posts and extract a style guide the drafting stage can be held to.
  2. 02Draft from a brief or a calendar slot, shaped by that style guide and by what the target platform allows.
  3. 03Check before a person is asked: run the guardrail battery — deterministic rules, model checks, and an injection scan on anything that came from a stranger.
  4. 04Gate: an approval is the only transition that puts an item in a queue.
  5. 05Publish approved items on schedule, exactly once, through the platform’s own API, under a rate governor.
  6. 06Ingest comments, dedupe and thread them, triage them, and draft replies that re-enter the same approval queue.
  7. 07Measure both halves: production signal from real approve and edit decisions, and an offline golden set that blocks a prompt change which regresses voice or safety.

07Architecture

A brief or a calendar slot is drafted against a voice profile extracted from imported posts. Every draft — a post, or a reply written from an ingested comment — passes the guardrail battery, then waits in the approval gate, which is the only transition that can queue it. Approved items are scheduled and published exactly once by Celery workers through the platform’s OAuth API, using tokens held in a Fernet-encrypted vault. Approval decisions feed the evaluation metrics.

CreateEngageControlBrief or slotComposer · calendarVoice profileStyle guideDraftingPlatform-awareGuardrailsRules · model · injectionApproval gateThe only way to queuePublishCelery · exactly onceComment ingestionPolled · webhookTriageDedupe · thread · classifyReply draftingSame voiceToken vaultEncrypted at restEvaluationEdit rate · safety
  1. 01Brief: a content idea or a calendar slot enters the composer.
  2. 02Voice profile: a style guide extracted from the person’s imported posts.
  3. 03Drafting: a platform-ready draft is generated against that style guide.
  4. 04Comments: incoming comments are ingested, deduplicated, threaded and triaged.
  5. 05Reply drafting: a reply is written from the comment, in the same voice.
  6. 06Guardrails: deterministic, model-based and prompt-injection checks run on every draft and reply.
  7. 07Approval gate: a human decision is the only transition into a queue.
  8. 08Publishing: Celery workers publish approved items on schedule, exactly once, through the platform API.
  9. 09Token vault: OAuth credentials are decrypted only at the moment of use.
  10. 10Evaluation: approval and edit decisions feed the voice and safety metrics.

08Features

  • Voice profiles

    A style guide extracted from posts the person already wrote, and editable afterwards.

  • Platform-aware drafting

    Drafts written to the target platform’s limits and conventions, not one generic post.

  • Guardrail battery

    Deterministic rules, model-based checks and a prompt-injection scan run before a person is asked to approve.

  • A gate, not a setting

    Approval is the only transition that queues an item, enforced in the schema and asserted by tests.

  • Publishes exactly once

    Scheduled work survives a worker restart: due times live in the database, and a missed slot is caught up or flagged.

  • Comment triage and replies

    Comments are deduplicated, threaded and classified; suggested replies go back through the same gate.

  • Encrypted token vault

    OAuth credentials are encrypted at rest, never logged and never returned by the API.

  • Measured, not assumed

    Edit rate per voice profile and safety pass rate in production, plus an offline golden set that gates prompt changes.

09Security and Reliability

Security controls

  • The approval gate is a state transition in the data model, not a permission flag: nothing reaches a publishing queue without it.
  • Platform OAuth tokens live in a Fernet-encrypted vault, are decrypted only at the moment of use, and are never logged or returned by the API.
  • Password hashing with bcrypt and JWT session tokens; every user-facing action writes to an audit log.
  • A prompt-injection scan on text that arrived from strangers — a comment is untrusted input, and one adversarial case proved it by getting a local model to obey it while the guardrail still blocked the reply.
  • Official platform APIs only, within each platform’s documented automation rules — no scraping and no headless browser sessions.
  • detect-secrets in CI, and security-invariant tests that assert every route requires authentication rather than trusting review to catch it — a pass that found two unauthenticated endpoints and a route sweep that was passing vacuously.

Reliability and observability

  • Due times live in the database, so the pipeline survives its own downtime: a missed slot is caught up or flagged rather than silently lost.
  • Publishing is idempotent — retries and reconciliation cannot turn one approved post into two.
  • A per-platform rate governor keeps the publisher inside each platform’s limits.
  • Liveness heartbeats and a staleness watchdog make a stalled queue audible, with an alert to the person whose work stopped moving.
  • The MVP acceptance criteria run as an executable test suite, and the full loop is exercised in one session — against a fake platform adapter, which is the honest limit of that evidence.

10Challenges

  • Style imitation is the part most likely to fail in a way that is worse than not trying — a near-miss on someone’s voice reads as inauthentic.

    The gate keeps a bad draft private, and the edit rate per voice profile turns “does this sound like me?” into something visible rather than a feeling.

  • Running the evaluation harness for the first time blocked the release: against the local development model, the safety gate failed on the adversarial cases, and one politely-worded prompt injection was obeyed outright.

    The guardrails caught every one of those failures, which is what the layered design predicts — but the run also settled the model question: a small local model is fine to build against and is not a candidate for drafting anything public. No baseline was saved, because a failing run must not become the bar.

  • Every platform-facing decision — endpoint shapes, payloads, API version pins, what each platform permits an automation to do — is unverifiable until app review clears.

    The integrations sit behind one adapter interface with a fake implementation, so the whole loop could be built and tested without waiting — while staying honest that this is the largest untested surface in the project.

11Decisions and Tradeoffs

DecisionAlternative consideredWhy
Make approval a state transition in the data model.A configurable auto-publish mode, or approval enforced in the service layer.Approval limits how much time the tool can save, and that is the whole point of the trade: the content stays the person’s own. Putting it in the schema means a future feature cannot quietly route around it — the cost is that convenience features like auto-approve rules become deliberate work rather than a flag.
Build the entire loop against a fake platform adapter.Waiting for platform app review before building the integration-dependent half.Nothing in the schedule waited on a review queue outside the project’s control, and CI stays offline and fast. In exchange, every platform-specific detail is unverified until credentials land, and the acceptance suite has to be re-run against a real sandbox account before any of it counts as working.
Run a small local model in development behind a provider abstraction.Developing directly against the hosted model intended for production.Local inference costs nothing, keeps sample posts on the machine, and lets the test suite fake the provider entirely. It also flatters nothing: the weaker model is what exposed how much the guardrails were actually carrying. The catch is that quality readings taken in development do not transfer, so the evaluation has to be re-run against the production model before its scores mean anything.

12Result

The full loop works end to end: import writing samples, extract a voice, draft a post or a reply, clear the guardrails, approve it by hand, and watch it publish on schedule — with comments coming back in, triaged, and answered through the same gate. A React front end covers all of it: composer, approval inbox, content calendar and settings.

It is honestly a prototype, not something shipped. Every platform interaction is verified against a fake adapter because the app reviews are outstanding, the evaluation harness has been run once and blocked on the local development model, and no production hosting exists. What that leaves is a complete, tested system with one unverified edge — and a written record of exactly where that edge is.

13Lessons Learned

  1. 01Deciding where the human approval step sits is a product decision that constrains the entire architecture around it — and putting it in the schema is what stops it eroding later.
  2. 02An evaluation harness earns its keep the first time it says no. Its first run blocked a release and answered the model-choice question at the same time.
  3. 03Guardrails should be designed to hold when the model fails, because it will: the case that mattered most was the one where the model obeyed an injection and the deterministic check caught it anyway.
  4. 04A fake adapter buys you a complete system quickly and leaves exactly one thing untested. That is a good trade only if you keep saying out loud which thing it is.

14Technology Stack

Language

  • Python
  • TypeScript

Backend

  • FastAPI
  • SQLAlchemy
  • Alembic
  • Celery + Redis
  • JWT authentication
  • OAuth 2.0 platform integrations
  • Webhooks

Data

  • PostgreSQL

Frontend

  • React
  • Vite

AI and retrieval

  • Writing-style extraction
  • Voice-matched generation
  • Provider abstraction over local and hosted models
  • Guardrail battery
  • Prompt-injection detection
  • Golden-set evaluation

Platform

  • Docker Compose
  • GitHub Actions CI

Practices

  • pytest
  • Ruff
  • mypy (strict)
  • detect-secrets
  • Sprint planning documents

15Screenshots

Screenshots to be added

Let’s discuss your project

Have a software, AI, RAG, or web application project in mind? Let’s discuss what you need and determine the right technical approach.