feat/ branches for your morning review. Nothing deployed without you.feat/oll-model-ollama-bundle, PR #55) is proven end-to-end for humaniz AND specview — real completions through a local model, in the browser, with Groq still the default. All in local Docker dev-test; nothing in production was touched.What I proved (local Docker, no prod):
ollama/ollama container, model qwen2.5:0.5b (397 MB) pulled. GET /api/text/health → {default_provider: groq, providers: {groq: ok, ollama: ok, claude: skipped}}. Baseline direct calls: provider=ollama → a real completion on qwen2.5:0.5b; no provider → Groq (llama-3.3-70b-versatile). Groq stays the default.feat/textops-gateway-client) booted in Docker, pointed at an Ollama-default gateway, with a stub Core for auth. POST /api/v1/text/rewrite returned provider="ollama", modelUsed="qwen2.5:0.5b" — a real rewrite. Made browser-clickable at http://localhost:8090/editor (Next.js frontend, same-origin build, a dev auth-inject proxy supplies the backend Bearer, the login gate neutralized for local).feat/oll-model-gateway-provider) booted against a local throwaway Postgres + the Ollama gateway (CHAIN_PROVIDER=gateway, SKIP_AUTH). The anonymous bootstrap (POST /api/ai/text/anonymous/bootstrap-project) completed through the web nginx proxy at http://localhost:8095 → api → ollama (3 gateway calls), producing real spec docs.502 {code,message,request_id} envelope, no crash). ⚠️ Worth noting: health's ollama: ok is config-based (OLLAMA_BASE_URL is set), not a live probe.Both products today default to whatever provider the gateway is configured with — a user can't choose Ollama (or any model) per request:
text_client supports a provider override, but the rewrite route / humanizer / DTO never plumb it through.{"messages"} and drops the model param that generate() already accepts.A user-facing model switch needs a small additive change in each. Two PRs are open for morning review (open, not merged, nothing deployed): (a) humaniz PR #14 (feat/textops-provider-select) — user-selectable provider/model through the rewrite path; 78 tests pass, ruff clean; base feat/textops-gateway-client. (b) specview PR #125 (feat/chain-provider-forward) — forward model/provider through the chain gateway adapter; 61 chain tests pass, ruff clean; base feat/oll-model-gateway-provider.
OLL_MODEL_SERVICE_TOKEN + GROQ_API_KEY); everything else has a prod-correct default and stays unset. Coolify app config + the one-time deploy gate + three curl smoke tests are all in the card.
anthropic_cloud, which has no access to the local ~/Projects repos. So the night shift runs in-session as PR-only work: every change lands on a feat/ branch as a PR for morning review, nothing deployed.
:8099 → backend → oll-model gateway 200 → model → rewrite; /editor loginless).ostris trainer hash, per-order destination model, sample the trained version directly. The empty-TODO model ids are now real. Verified no-spend (45 pytest + mocked-SDK wiring, docker boot, ready:true). Re-verified deploy-ready (06-30, fresh build+run): 47 pytest green · docker build + boot → /api/foto/health 200 · checkout & fulfill return clean {code,message,request_id} envelopes (no crash) · container stays healthy. The live round-trip pinpoints the one blocker: foto checkout → core.oll.am/api/billing/guest-checkout-session = 404 ⇒ foto's code is done; it just needs PR #21 (Core guest-checkout) merged + deployed to Core. No new build needed on foto.ANTHROPIC; it reports the gateway's real provider/model (verified: the round-trip returns ollama/llama3.2). On both humaniz branches (cleanly on the mergeable #9).model.oll.am (you deployed; verified groq:ok, authed /complete, 401) · humaniz #9 GREEN + mergeable · foto results-email #46 merged · deploy control #45 + oll-model CI gate #47 ready (both green)./status self-heal, off the critical path) has gone conflicting as main advanced — it needs a rebase before merge.OLL_MODEL_SERVICE_TOKEN on the humaniz backend (base defaults to model.oll.am) → merge humaniz #9 (it now also carries the honest-provider fix) → redeploy. This runs humaniz keyless on Groq AND un-breaks the 503 (the deployed #8 crash-loops on the removed ANTHROPIC_API_KEY; #9 needs no key). Then frontend build var NEXT_PUBLIC_API_URL → empty + rebuild.OLL_MODEL_BASE_URL=https://model.oll.am + the token on the specview api app → redeploy → kills the claude CLI error. ✅ #122 is admin-merge-safe (now confirmed by running): its red is a pre-existing test-isolation flake — the billing self-heal test passes 25/25 in isolation and only fails in CI's full-suite order; the gateway code is unaffected (evidence on the PR). Follow-up (CI-env only): make that test hermetic.core.oll.am/api/billing/webhook + STRIPE_WEBHOOK_SECRET) · review+merge Core guest-checkout #21 · merge foto #48 (real LoRA, CI green) · set a real REPLICATE_API_TOKEN for one true train+sample · a .ch domain + deploy foto. ⚠️ Still to build (next fire): a foto photo-intake/upload endpoint — checkout currently takes input_photo_urls directly, so a guest has no way to upload selfies yet (the one real code gap before foto can take a dollar).core.oll.am like specview does — humaniz.me (now done). Goal 2 = a new oll.am photo service built FROM trendfy's pipeline (not a repoint). speedback removed from scope.
🧩 Product feature — model picker, end to end (3 PRs):
feat/textops-provider-select) — backend threads an optional provider/model through the rewrite path; 55 tests. Base feat/textops-gateway-client.feat/textops-provider-select-ui) — editor UI: a "Model:" dropdown (Default cloud / Cloud Groq / Local Ollama); tsc + lint + build + vitest green (16 tests). Stacked on #14.feat/chain-provider-forward) — forwards model/provider through the chain gateway adapter; 29 tests. Base feat/oll-model-gateway-provider.feat/chain-provider-select, base feat/chain-provider-forward, stacked on #125) — adds the gateway-only guard (closes the latent non-gateway TypeError edge) AND exposes optional provider/model on both bootstrap routes (authed + anonymous) with an allow-list {groq, ollama, claude} and a clean 400 on unknown; 332 tests pass, ruff clean, OpenAPI valid. spec-gen RETRY-path threading deferred as a noted follow-up.feat/textops-gateway-client → #14 → #15; then specview's own stack bottom-up — feat/oll-model-gateway-provider → #125 → #127.🧪 Dev-tested gate — oll-am PR #56 (feat/e2e-model-path, base stage): scripts/e2e-model-path.sh + docker-compose.e2e.yml. A repeatable harness that boots gateway + Ollama in isolation and asserts the full contract — health (groq + ollama) · ollama completion on qwen2.5:0.5b · groq default · auth 401/403 · ollama-down → groq still serves + explicit ollama → clean 502. Ran twice: 16 passed / 0 failed → DEV-TESTED GATE: PASS. This is the candidate automated gate for the stage→main auto-promote plan. Caveat: based on PR #55, so its diff rides #55 until #55 lands on stage (#55 already auto-merged to stage).
🔍 Adversarial merge-readiness review on all three feature PRs returned ALL SHIP, no blockers: default/omit paths byte-for-byte unchanged, validation correct, backward-compatible signatures, no secret / SDK-boundary / error-envelope regressions. One carry-forward → now CLOSED: specview's adapter.generate(provider=…) would TypeError if a route passed provider while CHAIN_PROVIDER is non-gateway — the follow-up landed as specview PR #127 (the gateway-only guard) in the same change that exposes provider/model on both bootstrap routes.
🚀 Deploy prep — the oll-model live deploy card (documented in the platform architecture) stands: the gateway needs only 2 secrets at deploy; live smoke-test curls included. 📣 Distribution (P1) — a DRAFT build-in-public share kit is ready for review (not posted).
The night-shift milestone is complete to the autonomous boundary — everything buildable/testable as PR-only work is built, tested, reviewed, and stacked. What remains is yours: console moves (merges + deploy) and a few greenlight calls.
feat/textops-gateway-client → #14 → #15; specview: feat/oll-model-gateway-provider → #125 → #127) · arm the oll-model deploy gate (DEPLOY_MODEL_ENABLED + COOLIFY_HOOK_MODEL) + set the 2 secrets · run the deploy-card smoke tests · ship · and decide the auto-promote trigger (label vs button) + whether merge-to-main auto-deploys armed services.
core.oll.am/api/billing/webhook + set STRIPE_WEBHOOK_SECRET (console, ~3 min) → unlocks the existing CHF 9 Pro link.Why: Core is live and the price is set, but without the webhook a payment succeeds while the plan never flips to pro — the customer pays and gets nothing.
Exact setup (avoids the wrong-events footgun): in Stripe → Developers → Webhooks → add endpoint https://core.oll.am/api/billing/webhook, select these 6 events Core actually handles — checkout.session.completed · customer.subscription.updated · customer.subscription.deleted · invoice.payment_succeeded · invoice.payment_failed · invoice.upcoming — then copy the signing secret into Coolify env STRIPE_WEBHOOK_SECRET and redeploy Core. Verify: curl https://core.oll.am/api/health/stripe → ok, then send a test event from the Stripe dashboard → expect 200. (Keep the old test-mode webhook until a real live event proves on Core.)
PR #21 is built, additive (existing routes untouched, 0 deletions), and security-reviewed: a review caught a money-path underpayment bug (product-slug/price-fallback) which is now fixed and tested on the branch — strict product validation + a guest-only price lookup with no Pro fallback; 203 tests, contract + ruff + DTO-drift all green. Documented follow-ups for you (in the PR): set STRIPE_WEBHOOK_SECRET in prod, and the app-wide rate-limiter hardening (ProxyFix + Redis). It's NOT merged — your call.
Also review: PR #20 (this overnight work: foto-service scaffold + de-staled docs) and PR #18 (C2). Branch tangle: merge #18 only — standalone-services + deploy-topology would re-break live Core (see below).
Sam's immediate goal: do for humaniz.me what we did for specview — make it a thin client of the live core.oll.am (delete its Supabase auth + Stripe + email; route those to Core; keep the Claude humanizer). It's authed → no PR #21 needed.
✅ CODE-COMPLETE — humanize-me PR #7 (not merged/deployed). Phase 1 (backend): ported services/write/ adapters byte-identical, deleted Supabase auth + Stripe, kept the Claude humanizer, re-aimed prompts. 60 tests · docker build · curl green. Phase 2 (frontend): Next.js swapped to Core magic-link + Bearer (specview's flow in React), nginx auth-split → core.oll.am, pricing→Free+Pro. npm run build exit 0 · eslint clean · vitest 14 · supabase/detector-bypass greps clean.
✅ Local round-trip PASSED (full stack via docker compose, real nginx proxy, both containers healthy): GET / Next.js 200 · GET /api/health → backend 200 · GET /api/auth/me no-token → proxied to live core.oll.am → 401 (the split works over TLS) · product route returns the unified {code,message,request_id} envelope. Each service also independently build+test verified.
✅ FULL magic-link round-trip verified (local). Stood up a local Core with humaniz registered + repointed the running stack at it: via :8099, magic-link {product:"humaniz"} → {sent:true}; /login + /auth/verify pages 200; authed routes 401 without a token. Confirmed the INVALID_PRODUCT you hit is purely Core config, not a humaniz code bug — an unregistered product 400s, a registered one passes.
✅ Login UX bug fixed (PR #7, bc83d70). Sam's live test surfaced two real Core-integration bugs in the frontend: after magic-link verify the header needed a manual refresh, and the redirect bounced to /login?redirect=/editor. Root cause: the verify page used a client-side router.replace that served the /editor route prefetched before the cookie existed and didn't re-run useAuth. Fixed with a full-page window.location.replace(dest) (honors ?redirect=). Verified by running: docker build/lint pass; /editor no-cookie → 307→/login, with oll_token → 200, no bounce.
Money loop (in-app) also proven via stripe listen → local Core: checkout.session.completed → plan_write free→pro + welcome email.
🐛→✅ BLANK-PAGE-AFTER-PAY FIXED + VERIFIED (2026-06-29). Sam's live test: paying on remote humaniz redirected to http://localhost:8095/upgrade (blank). Two layers, both fixed: (1) host — Core hardcoded success_url={FRONTEND_URL}/upgrade (= specview's :8095); oll-am PR #22 resolves the base per-product. (2) path — humaniz had no /upgrade route; humaniz PR #8 adds a success page that polls Core's live status until the webhook flips the plan. Proven by build+run+curl+docker (PR #22 image on real Neon oll_core + Stripe test): product=humaniz → success_url=https://humaniz.me/upgrade?session_id=… ✅; product=oll_pro (control) → http://localhost:8095/upgrade unchanged (backward-compat). humaniz already sends product:'humaniz' in its checkout call. Both PRs are CI-green and additive — awaiting Sam's merge + redeploy (then live Stripe price/keys). Item (3) product-route-validates-before-auth (400 vs 401) is a cosmetic follow-up. Rewrite/AI (model 404) parked per Sam.
| Decision | Options | My recommendation |
|---|---|---|
| foto results-ready email (the last foto gap) |
A) foto-owned Resend sender for the results email · B) a Core guest-results endpoint guarded by a service token | A — foto-owned Resend. The results email fires after async generation, which Core's webhook can't carry, and Core's /email/send is deliberately recipient-scoped. Option B widens Core's auth surface (a new service-token path to review) to send mail to arbitrary guests. A keeps Core frozen + as-reviewed; the small "product has email code" exception is justified because this one email is genuinely a product concern. Revisit B only if 2+ products need post-purchase email. |
| Billing model (blocks 3 of 4 products) |
A) pay-once grants · B) subscriptions · C) both | A — pay-once. It matches the public positioning ("pay once, no subscription"), foto's one-time CHF 49, and the landing's CHF 9. Core already supports Stripe mode=payment; entitlement for a one-time buy is a grant, not a recurring plan. the humaniz braindump assumed monthly — start pay-once anyway (simpler, on-brand) and add subscriptions later only if usage/churn data demands it. |
| photo service from trendfy's pipeline | A) new oll.am-native service harvesting trendfy's LoRA engine · B) one-to-one repoint of trendfy.me | A — new native service (your call, 2026-06-29). Build oll.am's first native photo service FROM trendfy's pipeline (foto/headshots is its first cut); do NOT repoint the trendfy.me product. trendfy stays alive via PR #16 while its engine is harvested. This is next-next, after the humaniz lift-shift. |
These are reversible, low-stakes calls — pick the recommendation (or override) and I'll execute the build. The two irreversible/console actions (live webhook · greenlight PR #21) stay with you.
The docs had drifted behind reality. Here is what's actually true, verified against the live service and the git tree.
| Thing | Status | Evidence |
|---|---|---|
Core deployed at core.oll.am | Live | GET /api/health → HTTP 200 {"status":"ok"} over HTTPS (TLS already on) |
| C2 — specview on the remote Core | Proven | specview nginx.core-split.conf already proxies to https://core.oll.am; PR #18 handover says "C2 proven" |
| First-dollar money path | Needs Sam | Live Stripe webhook missing — see the callout above |
| Branch tangle | Drifted | PR #18 (CONFLICTING) + feat/standalone-services (+25) + docs/deploy-topology (+19); local main diverged from origin/main |
The drift is just state.json/build-log.html churn colliding across branches — not lost work. The consolidation section below (filled from a recon agent) gives the safe morning merge order. I did not attempt risky git surgery unattended.
All tests + CI confirmed: foto-service 19/19, write-service 20/20, PR #21 Core 203 (contract + ruff + DTO-drift). Product-services CI green on GitHub (foto · pytest + write · pytest SUCCESS — the scaffolds now gate PR #20). PR state: #21 MERGEABLE, all CI green (hardened) · #20 MERGEABLE, green · #18 CONFLICTING on churn files only (merge first; take origin/main for state.json + build-log.html). Site design gate green (50/50 pages). Core live probe: HTTP 200.
Core stopped being a plan tonight and became live infrastructure. That flips the whole program: the remaining work is no longer "build Core" — it's "point everything at Core." Every product Sam has shipped carries its own duplicated auth, billing, and email. Core deletes all of that, once.
Each product becomes ~50–200 lines of product logic. The pattern proven by specview (C2) is now copy-paste for the rest. The sequencing stays revenue-first: the headshot product (foto-service) is the next dollar because it's closest to cash — guest checkout, no login, email delivery. After it, the existing live products migrate one by one.
This is the live record of the autonomous run. The session re-wakes via workflow completions + a :09/:39 "continue overnight" cron, working this queue until rate limits hit. Everything lands on feat/overnight-multiproduct-core as commits/PRs.
| # | Task | Status | Output |
|---|---|---|---|
| 0 | De-drift: verify Core live, sync state.json, capture directive in memory | Done | state/state.json, memory overnight-autonomous-run |
| 1 | Stand up persistence + self-sustaining loop (3 crons) | Done | crons: :47 commit+push · :37 digest · :09/:39 continue |
| 2 | Recon: per-product Core-migration briefs + branch-consolidation analysis | Done | 5-agent workflow → filled the two sections below (incl. the live-Core regression warning) |
| 3 | This ledger page (visible decisions) | Live | site/oll-am-overnight-plan.html |
| 4 | Scaffold foto-service (next-dollar headshot) on Core | Building | services/foto/ — Core client + Replicate boundary + SQLite jobs + tests (agent in flight) |
| 5 | Dev test: live Core + guest-checkout feasibility | Done | Core health/neon/stripe all 200; verdict: guest checkout needs a small Core guest-endpoint pair (see foto section) |
| 6 | Docs cleanup: link every page to the timeline (no orphans) | Done | render_index → 46 pages categorized, 0 Unfiled |
| 7 | Migration playbook (specview reference + skill library) | Done | section below — every migration mapped to a house skill |
| 8 | Scaffold foto-service + run its tests | Done | services/foto/ committed · 12/12 pytest green |
| 9 | Core guest-checkout PROPOSAL (additive, not merged) → PR #21 | Ready | additive guest-checkout-session + guest-verify-session + webhook-driven guest email; existing routes untouched; 194 tests (+19), ruff + contract + Schemathesis + GitHub CI all green. Awaiting Sam greenlight. |
| 10 | Adversarial review of the foto scaffold → fixes applied + tested | Done | fixed: idempotency race (atomic claim), HTTP error codes, wildcard CORS; 16/16 tests (was 12). 3 blockers are Core-side (guest endpoints). foto-service page |
| 11 | New skill: guest-checkout-grant | Done | no-login one-time-sale pattern + the 4 gotchas, in ~/.claude/skills/ |
| 12 | Core Client Kit standard (frontend half of a Core client) | Done | page — generated client + 3 interceptors, grounded in specview's real code; the recipe every product frontend copies |
| 13 | trendfy 504 diagnosis (read-only) | Done | boot-hang on suspended Neon; console revival + crash-proof PR documented above; corrected a false "burned key" claim (nothing committed) |
| 14 | Doc fact-check → 21 pages de-staled to current reality | Done | reality banners + fixed "guest-checkout-works-today" residue + api.oll.am→core.oll.am; gate green |
| 15 | Wire foto-service to the PR #21 guest contract | Done | guest endpoints + job_id-via-success_url correlation (dropped fragile cs_-parse), payment gated on paid; 19 tests. Remaining gap: results-ready email (recipient-scoping). |
| 16 | write-service scaffold (humaniz → authed product pattern) | Done | thin Core client: Bearer → Core /me → plan-gate → Claude (llm-call-contract); honest-writing positioning enforced; 15 tests. Both Core-client patterns now exist (foto=guest, write=authed). |
| 17 | Adversarial review of write-service → fail-open auth fixed | Done | review found get_me served stale pro on a Core 401 (cancelled user keeps pro). Now fails closed on 401/403 (evict+raise), serves stale only on real outages; +5 tests (20/20). |
| 18 | Adversarial review of PR #21 → HIGH money-path bug found → FIXED | Done | H1 (underpayment via slug-mismatch + Pro-price fallback) fixed: strict product ^[a-z0-9_]+$ DTO + guest-only price lookup that raises (no fallback). Additive, authed path untouched; 203 tests (+9), contract + ruff + DTO-drift green. M1/L1-L3 documented in the PR for Sam. |
| 19 | trendfy boot-resilience PR → trendfy PR #16 (crash-proof the 504) | Done | non-fatal init_db + connect_timeout(5s) + $PORT + stdlib HEALTHCHECK; migrations gated behind RUN_MIGRATIONS=1. Verified: boots ~11s bounded (not hanging) against an unroutable DB, health 200, degrades. 49 pass / 3 fail (3 pre-existing on master). Additive, not merged. Deploy note: set RUN_MIGRATIONS=1. Still need the console revival (wake Neon + redeploy). |
| 20 | CI for the product services (gates fail the build) | Done | .github/workflows/services.yml — matrix runs foto + write pytest on services/** changes, so the scaffolds' tests now gate PR #20 (the house executable-arch-rules pattern). |
| 21 | Re-review foto (post-rewire) → BLOCKER fixed | Done | The guest rewire was unreviewed; review caught a revenue exploit — fulfill trusted only paid, so a CHF 1 waitlist session could fulfil a CHF 79 order (Core's guest-verify has no ownership gate). Fixed: bind session→order by product+plan+guest+amount+currency; +session-reuse guard + IntegrityError recovery (no stuck-job 500); input cap + MAX_CONTENT_LENGTH; conn-leak. 20 tests (+ exploit regression test). |
| 22 | Sam-gated: spec-service · foto results-email fork · pay-once-vs-subscription · trendfy standalone-vs-bubls | Awaiting Sam | need your decision/console (recommendations above) |
| 13 | Morning verification kit + green gate + PR | Live | checklist below · PR #20 |
| 26 | ADR-020 pattern promoted to the llm-call-contract skill | Done | Idle-budget invested in tooling, not repo churn: encoded the proven provider-switch + provider-aware boot-gate + single-SDK-boundary executable-rule (from PR #29) into the reusable skill so future text products inherit it. No repo PR (skill library only). Re-verified the gated path unchanged (#22 + humaniz #8 OPEN, /upgrade 404). |
| 27 | humaniz Core-lift GUI round-trip — VERIFIED in Docker (frontier a) | Done | Frontend Core migration (magic-link+Bearer+nginx split) was already merged (PR #7) → Phase 2 code-complete; closed the verify-by-running gate. Repaired the bit-rotted keyless devtest stack (removed-worktree + old text-ops name → now self-contained against the current services/oll-model + an in-repo stub). Green end-to-end: nginx :8099 → backend /api/v1/text/rewrite (DEV_AUTH, no login) → gateway POST /api/text/complete 200 → model → marked rewrite; /editor loginless. humaniz feat/dev-auth pushed (ff7d8d9). Found (follow-up): backend hardcodes provider:"ANTHROPIC" though the gateway owns the provider — text_client.complete() should surface the gateway's provider/model. |
| 28 | foto real LoRA pipeline lifted verbatim from wardrobai (frontier b) | Done | PR #48. foto's replicate provider was a guessed 3-id shape with EMPTY model ids (spec gap); lifted the working wardrobai/server/services/{training,generation}.py verbatim: pinned ostris/flux-dev-lora-trainer hash, per-order private destination model (idempotent), trainer input byte-for-byte (zip→1024 via Pillow, steps=2000/rank=16/adamw8bit/lr=0.0004/autocaption), and the SAMPLER is the trained version itself (LoRA baked in — no separate base model, the core mis-shape fixed; guidance_scale corrected 7.5→3.5). Guard unchanged (mock default). Verified no-spend: 45 pytest incl. mocked-SDK verbatim-param wiring, ruff clean, docker build (Pillow) + boot healthy tokenless, real provider ready:true. Core untouched. Sam to take the first foto franc: merge PR #21 (guest checkout) + set a real REPLICATE_API_TOKEN for one true train+sample + a .ch domain/deploy. |
| 29 | Honest provider — humaniz reports the real model, not ANTHROPIC (frontier c) | Done | Closed the follow-up from #27: the rewrite/generate response hardcoded provider:"ANTHROPIC" though the oll-model gateway now owns the provider switch. Fixed additively (text_client Completion + complete_meta(); complete() a str shim so streaming is untouched; create_message_meta(); humanize_single/_multipass return the gateway meta; 3 response sites report the real provider/model; cache hit → oll-model/(cached)). Verified by build+run+docker: 74 pytest + ruff green; GUI round-trip now returns provider:"ollama", modelUsed:"llama3.2" (not ANTHROPIC). Landed on both humaniz branches — cherry-picked cleanly onto the mergeable #9 (OllModelError naming, 68 pytest green) + #10. |
Each live product carries its own duplicated auth + billing + email. Core deletes that, once. Below: what's actually in each codebase, what Core replaces, the effort, and the path to its next paying customer. Order = revenue-proximity.
| Product | Code | Today (auth · billing · email) | Effort | Next-$ path |
|---|---|---|---|---|
| foto-service headshots (.ch) | landing 85% (Bewerbungsfoto/) + pipeline reused from wardrobai/server | own JWT (dropped — guest) · own Stripe (guest /checkout/public) · own Resend | M-L | NEXT dollar — landing live → CHF 49 guest checkout via Core → Replicate LoRA → Core emails photos |
| trendfy.me AI fashion shoots | local: 2026/wardrobai (remote trendfy.git) | — (pipeline donor, not repointed) | next-next | its LoRA engine → oll.am's first native photo service (not a one-to-one repoint); kept alive via PR #16 while harvested |
| humaniz.me honest writing tool | 2026/products/humanize-me ✓ | Supabase auth · Stripe 4-tier subs · Supabase emails | M-L | largest TAM; free tier → Core checkout unlock; honest-clarity framing only |
The LoRA pipeline already exists and works (13+ models trained) inside wardrobai/server. The job is mostly delete auth/billing/email + wire 3 Core HTTP calls + guest gating + deploy. Flow: landing → CHF 49 tier → Core checkout → success_url returns to upload → Core verify-session gates it → upload 5-15 selfies → Replicate trains + generates → Core email/send delivers. A background agent scaffolded services/foto/ tonight (thin Core client + Replicate boundary + SQLite job store, all house-pattern).
A code-level investigation (read core/modules/billing line-by-line) gives a definitive verdict: Core as deployed cannot do guest checkout. Both create-checkout-session and verify-session are @require_auth (401 without a JWT); verify-session is ownership-locked (403 on mismatch) and returns only {plan, payment_status} — no buyer email/metadata; the webhook no-ops when metadata.user_id is absent (a guest has none). Good news: one-time payment mode already works (plan:"one_time" → Stripe mode=payment, price STRIPE_PRICE_HEADSHOT_CHF49_ONE_TIME). The blocker is purely identity.
Minimal fix (bounded, ~1 route pair): add guest variants on a feat/ branch — POST /api/billing/guest-checkout-session (no auth; takes email + per-product-allowlisted success_url/cancel_url; sets customer_email + metadata.guest=true, no client_reference_id) and GET /api/billing/guest-verify-session (no auth/ownership; returns paid, email, amount_total, metadata). Guest email is delivered from the webhook via the email service layer (sidesteps the HTTP route's recipient scoping — no auth surface widened).
✅ Built as PR #21 (additive, NOT merged): 194 tests (+19), ruff + contract + Schemathesis + GitHub CI all green; existing authed routes byte-for-byte unchanged. Your move: review + greenlight + merge PR #21 → deploy Core → foto-service can sell.
Decision (Sam, 2026-06-29): we do not repoint trendfy.me one-to-one onto Core. Instead its proven LoRA engine becomes oll.am's first native photo service (foto/headshots is its first cut) — next-next, after the humaniz lift-shift. trendfy.me stays alive via PR #16 while its pipeline is harvested.
Backend is down with a 504. Most likely a boot hang on a suspended/unreachable Neon DB: create_app() calls init_db() synchronously and psycopg2.connect has no connect_timeout, so against autosuspended free-tier Neon it hangs forever (a hang gives 504, not the 502 a fast crash would). restart: unless-stopped makes it loop, never self-healing.
Console revival (Sam, ~5 min): read the server logs in Coolify; wake/repoint the Neon compute behind DATABASE_URL (update the env if creds rotated); confirm the other env (REPLICATE_API_TOKEN, ANTHROPIC_API_KEY, etc.); redeploy → watch for "DB pool initialised" + a 200 on /api/v1/health. Follow-up PR (crash-proof): non-fatal init_db + connect_timeout, gate migrations out of import, add $PORT + a HEALTHCHECK — the exact hardening already proven on Core. Secrets: nothing is committed (.env is gitignored, never in git history; the local Stripe key is sk_test_, not live) — no rotation forced.
The LoRA pipeline is confirmed clean to port to foto-service: services/{generation,training,profile,prompts,pipeline}.py + storage/ are framework-agnostic ("no Flask"); auth/billing/email stay behind and come from Core.
2026/products/humanize-meRepo located: 2026/products/humanize-me (remote humanize-me-clean.git) — a full app (backend + frontend + supabase + contracts + docker-compose), so the migration is fully groundable (Supabase auth, Stripe subs; keep the Flask+Claude rewrite logic → becomes write-service). Hard ethics constraint: the braindump leans on detector-bypass framing (Turnitin/GPTZero); every line of copy must be re-framed to honest writing-improvement only. Next: audit the repo, then apply the write-service template.
Billing model: Core's entitlement is plan-from-/me (subscription-shaped), but oll.am's public positioning is "pay once." trendfy sells a one-time photoshoot; the humaniz braindump assumes monthly subs. Decide once: does Core express a one-time purchase grant, or do products keep purchase-grant logic while Core owns only checkout/webhook? foto-service hits it first (guest one-time CHF 49).
feat/standalone-services and docs/deploy-topology forked from old main (pre-PR #10). Merging either would silently revert the deploy fixes that made Core go green: core/Dockerfile loses the $PORT bind + HEALTHCHECK (the Coolify 502 fix, 9ea90a1) and health.py re-adds the stripe Balance.retrieve(timeout=5) kwarg main deliberately removed (34596f2). Their big diffs are mostly illusion — churn + false "deletions" of scripts main added later.
| Branch | Real unique value | Action |
|---|---|---|
| feat/c2-core-split (PR #18, +12) | 3 new doc pages (env-secrets · magic-link-routing · specview-core-cleanup) + a real core/.env.example rewrite. Touches no core/ code — zero regression risk. Forked from recent main. | MERGE 1st — resolve 3 churn conflicts (take origin/main for state.json + build-log.html; reconcile handover.html prose) |
| feat/standalone-services (+25) | Only ~3 real commits: per-service Dockerfiles, Coolify deploy guide, site/→landing/ rename. The rest is churn or already in main. | DON'T MERGE — re-author those 3 commits FRESH on current main during the foto-service phase, then abandon |
| docs/deploy-topology (+19) | Exactly ONE unique commit b6d5d82: a deploy-topology brainstorm page. Strict subset of standalone otherwise. | ABANDON — optionally cherry-pick b6d5d82 onto a fresh branch if the brainstorm is worth keeping |
Safe morning sequence: (1) merge PR #18, resolving the 3 churn conflicts; (2) optionally cherry-pick b6d5d82; (3) abandon docs/deploy-topology; (4) leave standalone-services unmerged, harvest its 3 infra commits fresh later. origin/main (ff20fbe, PR #19) already has the Core lift, contract slice, and all deploy fixes — it is the source of truth.
We don't invent each migration. specview already became a Core client (C2), and the house skill library encodes every move as a reusable, gated procedure. Every product migration is the same spine: prove the seam the way specview did, applying these skills in order.
| Migration concern | House skill to apply | specview reference (the proof it works) |
|---|---|---|
| Carve the slice out of a monolith | extract-service-from-monolith | Core itself was lifted out of specview; specview became its first HTTP client |
| The frozen API seam + typed clients + drift gate | contract-first-api | core/openapi.yaml → generated DTOs + TS-Angular client + Schemathesis CI gate |
| Auth over HTTP (Bearer, not cookies) | jwt-bearer-spine + magic-link-auth | specview's auth.interceptor.ts + Core @require_auth; trendfy already decodes the same JWT shape |
Entitlement = identity in token, plan from /me | token-identity-not-entitlement | Core JWT carries only sub+email; plan read live from /me |
| Billing: checkout + webhook + return page | stripe-billing-wiring · inbound-webhook-handler · checkout-return-page | Core owns Stripe; specview calls create-checkout + verify-session |
| Transactional email via Core | transactional-email + http-service-adapter | Core /email/send (Resend behind one boundary) |
| Web-vs-native API host | env-injected-base-url | same build → same-origin /api on web, absolute origin on Capacitor |
| The nginx split (product vs Core) | (specview pattern) | nginx.core-split.conf — auth/billing/email → core.oll.am, product → local API |
| Uniform errors + observable side-effects | error-envelope + audit-outcome-logging | Core's {code,message,request_id} envelope |
| A realistic test DB without risking prod | neon-fork-for-local-testing | Core runs on oll_core, forked from prod |
| Product LLM calls (e.g. write-service) | llm-call-contract | Claude is the house LLM; prompts as constants, JSON contract, truncation caps |
The foto-service scaffold built tonight already follows this: a single core_client.py boundary (http-service-adapter), a replicate_client.py boundary, an {code,message,request_id} error envelope mirroring Core, and a SQLite job store (local-first/filesystem per the data decision — Neon is Core-only).
Work this top-to-bottom when you wake:
core.oll.am/api/billing/webhook, set STRIPE_WEBHOOK_SECRET in Core's Coolify env, redeploy Core.curl https://core.oll.am/api/health → expect {"status":"ok"}.pro (this is what the webhook enables).feat/overnight-multiproduct-core PR (this page + scaffolds) and any service-scaffold PRs.origin/main; main checkout left to its hooks/cron.