Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Ben Bracamonte

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
37 changes: 25 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ Etsy trends → Scout → Design → Listing → (Etsy ↔ Printify fulfillment)

**Scout** runs nightly. It scans Etsy for trending niches, calls Claude to extract structured trend briefs (keywords, price targets, color palette), and writes them to `trend_briefs`. Exact-match and semantic dedup via Claude prevent near-duplicate briefs from flooding the pipeline.

**Design** polls every 15 minutes. It picks up pending briefs, calls Claude to craft a FLUX-safe image prompt, generates a 300dpi print-ready PNG via fal.ai (FLUX Pro 1.1), and uploads the result to Supabase Storage. Identical prompts are detected by SHA-256 hash and reuse the prior image rather than paying for a duplicate fal.ai call.
**Design** polls every 15 minutes. It picks up approved briefs, calls Claude to craft an image prompt (or uses the operator-authored `image_description` verbatim when present), generates a 300dpi print-ready PNG via fal.ai, and uploads the result to Supabase Storage. The image backend is selected per-brief: `fal_gpt_image_2` (OpenAI gpt-image-2, the default), `fal_flux_pro` (FLUX Pro 1.1), or `fal_nano_banana_2` (Gemini-3). Identical prompts are detected by SHA-256 hash and reuse the prior image rather than paying for a duplicate fal.ai call.

**Listing** polls every 15 minutes. It creates a hidden Printify product to generate mockup images, writes SEO-optimized listing copy via Claude, then publishes to Etsy (draft → images → activate). If `HUMAN_REVIEW_ENABLED=true`, listings pause at `needs_review` for manual approval before going live.
**Listing** polls every 15 minutes. It creates a hidden Printify product to generate mockup images, writes SEO-optimized listing copy via Claude, then pauses every listing at `needs_review`. There is no auto-publish path: an operator approves the listing in the dashboard, which flips it to `pending_publish`, and the next Listing run drives the Etsy publish (draft → inventory → images → activate). The full Etsy seller-policy compliance gate runs twice — once before copy generation and again at publish time.

**Ledger** polls Etsy receipts every 30 minutes for paid orders. Each receipt is logged once (idempotent via a unique constraint on `etsy_order_id`) with sale price in buyer currency, USD-normalized total, computed Etsy fees, looked-up print cost, derived margin, and buyer country. A low-margin Slack warning fires per-order; a separate daily cron emits a revenue/margin digest via Slack and email.

Expand All @@ -29,11 +29,13 @@ Etsy trends → Scout → Design → Listing → (Etsy ↔ Printify fulfillment)
|---|---|
| Scout + Design | Python 3.12, httpx, pydantic, structlog |
| Listing + Ledger | TypeScript / Node.js 20, Zod, Bottleneck |
| AI reasoning | Claude Sonnet 4 (`claude-sonnet-4-20250514`) |
| Image generation | fal.ai — FLUX Pro 1.1 |
| Dashboard | Next.js (TypeScript), Supabase Realtime |
| Scout/Design reasoning | Claude Sonnet 4 (`claude-sonnet-4-20250514`) |
| Listing copy | Latest flagship Claude — currently Opus 4.8 (`claude-opus-4-8`) |
| Image generation | fal.ai — gpt-image-2 (default), FLUX Pro 1.1, or Gemini-3 (per-brief) |
| Database + Storage | Supabase (Postgres) |
| Print fulfillment | Etsy's native Printify integration (out of band) |
| Hosting + cron | Railway |
| Hosting + cron | Railway (planned — not deployed; agents currently run manually) |
| Alerts | Slack incoming webhooks + Resend email |

---
Expand All @@ -48,10 +50,12 @@ presswork/
│ ├── scout/ # Agent 1 (Python) — Etsy trend scraper
│ ├── design/ # Agent 2 (Python) — fal.ai image generation
│ ├── listing/ # Agent 3 (TypeScript) — Etsy listing publisher
│ └── ledger/ # Agent 4 (TypeScript) — receipt polling + economics digest
│ ├── ledger/ # Agent 4 (TypeScript) — receipt polling + economics digest
│ └── dashboard/ # Next.js control plane — review gates, manual triggers
├── infra/
│ ├── railway.toml
│ ├── railway.toml # planned service definitions (cron schedules commented out)
│ └── supabase/migrations/
├── docs/ # printify integration notes + docs/internal/ planning archive
├── tests/e2e/ # Full-pipeline smoke test
└── .github/workflows/ # CI — lint, typecheck, unit tests
```
Expand Down Expand Up @@ -102,7 +106,8 @@ See `.env.example` for the full list. Key ones:
| `PRINTIFY_API_TOKEN` / `PRINTIFY_SHOP_ID` | Printify credentials |
| `SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` | Supabase project |
| `SLACK_WEBHOOK_URL` | Incoming webhook for error alerts (optional) |
| `HUMAN_REVIEW_ENABLED` | `true` to pause listings before publishing (default) |
| `ETSY_MOCK_MODE` | `true` runs the full publish flow against canned Etsy fixtures (no Etsy creds needed) |
| `DASHBOARD_ALLOWED_EMAILS` | Comma-separated owner allowlist for dashboard sign-in |

---

Expand Down Expand Up @@ -162,7 +167,7 @@ Integration tests and the E2E smoke test are not run in CI — they require a li

## Database schema

Five tables — agents communicate exclusively through Supabase, never by calling each other directly.
Four core tables — agents communicate exclusively through Supabase, never by calling each other directly.

| Table | Written by | Read by |
|---|---|---|
Expand All @@ -177,17 +182,19 @@ Status columns enforce strict one-way transitions (`pending → processing → d

## Deployment

Each agent runs as a separate Railway service. Railway auto-deploys from `main` on push. Configure environment variables in the Railway dashboard — never commit secrets.
> **Status: manual only.** Nothing in this system runs on a schedule today. No cron is deployed and no agent fires on its own — every agent is a one-shot, drain-and-exit process invoked by the operator (see "Running agents manually" above). The table below is the *planned* Railway topology for if/when unattended automation is deliberately turned on; the schedules in `infra/railway.toml` are commented out.

| Service | Type | Schedule |
The intended layout is one Railway service per agent, auto-deploying from `main` on push, with environment variables configured in the Railway dashboard — never committed.

| Service | Type | Schedule (planned) |
|---|---|---|
| `scout` | Cron | Nightly at 2am |
| `design` | Cron | Every 15 min |
| `listing` | Cron | Every 15 min |
| `ledger-cron-receipts` | Cron | Every 30 min |
| `ledger-cron-daily-digest` | Cron | Daily at 13:00 UTC |

> **Note:** Etsy API access requires a separate storefront application. The pipeline runs fully against mocks until live credentials are available. Flip `HUMAN_REVIEW_ENABLED=false` only after manually verifying a listing end-to-end in a sandbox shop.
> **Note:** Etsy API access requires a separate storefront application. The pipeline runs fully against mocks (`ETSY_MOCK_MODE=true`) until live credentials are available. Every agent's output stays human-gated regardless of deployment — listings always pause at `needs_review` until an operator approves them in the dashboard.

---

Expand All @@ -201,3 +208,9 @@ Etsy fees (at $24.99): ~$2.12
Net margin at $24.99: ~$9.87 (39%)
Pricing floor: $21.25 (2.5× print cost)
```

---

## License

[MIT](./LICENSE) © Ben Bracamonte
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading