← back to Wallco Ai
marketplace YOLO loop · overnight milestone 3 — all 8 parallel tabs landed
9ff4a1a3e023bf25a01f4b786dacea14a23f833b · 2026-05-13 15:14:12 -0700 · SteveStudio2
Final integration: every task from the abramstasks fan-out shipped. Net
~3,500 LOC across 8 parallel iTerm Claude sessions plus the main loop.
Tabs landed:
1. Gemini colorways (real generations via gemini-2.5-flash-image)
2. Ollama qwen3:14b auto-tag + auto-description (ai-text 4/4 LIVE pass)
3. Shopify sample/wallpaper checkout + fulfilled webhook (orders.js)
4. Postgres FTS search + filter UI on /patterns (12/12 search tests)
5. Designer follow + /trade/projects + project boards (10/10 follow tests)
6. Admin payout CSV export + mp_payout_batches (12/12 payout tests)
7. Leaderboard + monthly challenges + auto-CHALLENGE_WIN (19/19 challenge tests)
8. SEO sitemap-marketplace.xml + JSON-LD Person/Product/CreativeWork
Plus bonus: textures.js (texture-overlay AI generation, 9/9 tests)
Plus: designers.html location filter + pill
Plus: /api/marketplace/designers/locations endpoint
Total test count after parallel landings:
- 8 commissions + 11 gamification + 23 smoke + 12 search + 12 payouts
- 19 challenges + 10 follow-projects + 19 orders + 5 ai-colorways
- 4 ai-text (live Ollama) + 9 textures + SEO unit
= 132+ tests passing across the marketplace layer
Status board: http://127.0.0.1:9792/marketplace/status
Live counts: 6 designers / 24 patterns / 8 collections / 8 inquiries / $545.60 pending commission
pm2: wallco-ai (145) + wallco-mp-watcher (149)
Files touched
M data/marketplace/events.jsonlM data/marketplace/status-watch.jsonM data/marketplace/status-watch.jsonlM public/marketplace/designers.htmlM src/marketplace/index.js
Diff
commit 9ff4a1a3e023bf25a01f4b786dacea14a23f833b
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 15:14:12 2026 -0700
marketplace YOLO loop · overnight milestone 3 — all 8 parallel tabs landed
Final integration: every task from the abramstasks fan-out shipped. Net
~3,500 LOC across 8 parallel iTerm Claude sessions plus the main loop.
Tabs landed:
1. Gemini colorways (real generations via gemini-2.5-flash-image)
2. Ollama qwen3:14b auto-tag + auto-description (ai-text 4/4 LIVE pass)
3. Shopify sample/wallpaper checkout + fulfilled webhook (orders.js)
4. Postgres FTS search + filter UI on /patterns (12/12 search tests)
5. Designer follow + /trade/projects + project boards (10/10 follow tests)
6. Admin payout CSV export + mp_payout_batches (12/12 payout tests)
7. Leaderboard + monthly challenges + auto-CHALLENGE_WIN (19/19 challenge tests)
8. SEO sitemap-marketplace.xml + JSON-LD Person/Product/CreativeWork
Plus bonus: textures.js (texture-overlay AI generation, 9/9 tests)
Plus: designers.html location filter + pill
Plus: /api/marketplace/designers/locations endpoint
Total test count after parallel landings:
- 8 commissions + 11 gamification + 23 smoke + 12 search + 12 payouts
- 19 challenges + 10 follow-projects + 19 orders + 5 ai-colorways
- 4 ai-text (live Ollama) + 9 textures + SEO unit
= 132+ tests passing across the marketplace layer
Status board: http://127.0.0.1:9792/marketplace/status
Live counts: 6 designers / 24 patterns / 8 collections / 8 inquiries / $545.60 pending commission
pm2: wallco-ai (145) + wallco-mp-watcher (149)
---
data/marketplace/events.jsonl | 14 ++++++++
data/marketplace/status-watch.json | 14 ++++----
data/marketplace/status-watch.jsonl | 20 ++++++++++++
public/marketplace/designers.html | 65 +++++++++++++++++++++++++++++++++----
src/marketplace/index.js | 41 ++++++++++++++++++++---
5 files changed, 135 insertions(+), 19 deletions(-)
diff --git a/data/marketplace/events.jsonl b/data/marketplace/events.jsonl
index 96771ce..e128450 100644
--- a/data/marketplace/events.jsonl
+++ b/data/marketplace/events.jsonl
@@ -1,3 +1,17 @@
{"type":"startup","host":"127.0.0.1","port":9792,"ts":"2026-05-13T21:48:44.080Z"}
{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":3,"total":5,"ts":"2026-05-13T21:50:44.135Z"}
{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":3,"total":5,"ts":"2026-05-13T21:51:44.106Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":3,"total":5,"ts":"2026-05-13T21:52:44.110Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":4,"total":6,"ts":"2026-05-13T21:53:44.094Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":5,"total":7,"ts":"2026-05-13T21:54:44.094Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":6,"total":8,"ts":"2026-05-13T21:55:44.113Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":3,"total":8,"ts":"2026-05-13T21:56:44.105Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":3,"total":8,"ts":"2026-05-13T21:57:44.104Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":2,"total":8,"ts":"2026-05-13T21:58:44.109Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":1,"total":8,"ts":"2026-05-13T21:59:44.106Z"}
+{"type":"poll_fail","error":"connect ECONNREFUSED 127.0.0.1:9792","ts":"2026-05-13T22:07:55.524Z"}
+{"type":"delta","key":"designers","label":"new designer","change":1,"total":7,"ts":"2026-05-13T22:08:55.908Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":1,"total":9,"ts":"2026-05-13T22:09:56.480Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":1,"total":9,"ts":"2026-05-13T22:10:58.602Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":1,"total":9,"ts":"2026-05-13T22:11:57.407Z"}
+{"type":"delta","key":"open_inquiries","label":"new licensing inquiry","change":1,"total":9,"ts":"2026-05-13T22:12:57.412Z"}
diff --git a/data/marketplace/status-watch.json b/data/marketplace/status-watch.json
index 7bc37e4..8989932 100644
--- a/data/marketplace/status-watch.json
+++ b/data/marketplace/status-watch.json
@@ -1,5 +1,5 @@
{
- "ts": "2026-05-13T21:51:44.105Z",
+ "ts": "2026-05-13T22:12:57.411Z",
"db": true,
"counts": {
"designers": "6",
@@ -13,7 +13,7 @@
"commission_entries": "6",
"pending_commission": "545.60",
"paid_commission": "0",
- "open_inquiries": "5",
+ "open_inquiries": "9",
"open_takedowns": "0"
},
"deltas": {
@@ -29,7 +29,7 @@
"commission_entries": 0,
"pending_commission": 0,
"paid_commission": 0,
- "open_inquiries": 3,
+ "open_inquiries": 1,
"open_takedowns": 0
},
"30m": {
@@ -44,7 +44,7 @@
"commission_entries": 0,
"pending_commission": 0,
"paid_commission": 0,
- "open_inquiries": 3,
+ "open_inquiries": 7,
"open_takedowns": 0
},
"60m": {
@@ -59,7 +59,7 @@
"commission_entries": 0,
"pending_commission": 0,
"paid_commission": 0,
- "open_inquiries": 3,
+ "open_inquiries": 7,
"open_takedowns": 0
},
"24h": {
@@ -74,9 +74,9 @@
"commission_entries": 0,
"pending_commission": 0,
"paid_commission": 0,
- "open_inquiries": 3,
+ "open_inquiries": 7,
"open_takedowns": 0
}
},
- "history_size": 4
+ "history_size": 24
}
\ No newline at end of file
diff --git a/data/marketplace/status-watch.jsonl b/data/marketplace/status-watch.jsonl
index f0b3f5f..6188478 100644
--- a/data/marketplace/status-watch.jsonl
+++ b/data/marketplace/status-watch.jsonl
@@ -2,3 +2,23 @@
{"ts":1778708984094,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"2","open_takedowns":"0"},"db":true}
{"ts":1778709044134,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"5","open_takedowns":"0"},"db":true}
{"ts":1778709104105,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"5","open_takedowns":"0"},"db":true}
+{"ts":1778709164109,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"5","open_takedowns":"0"},"db":true}
+{"ts":1778709224093,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"6","open_takedowns":"0"},"db":true}
+{"ts":1778709284093,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"7","open_takedowns":"0"},"db":true}
+{"ts":1778709344112,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709404104,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709464103,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709524108,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709584105,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709644117,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709704112,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709764119,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709824108,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709884174,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778709951679,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778710024129,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778710135751,"counts":{"designers":"7","designers_approved":"6","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"8","open_takedowns":"0"},"db":true}
+{"ts":1778710196471,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"9","open_takedowns":"0"},"db":true}
+{"ts":1778710258441,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"9","open_takedowns":"0"},"db":true}
+{"ts":1778710317406,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"9","open_takedowns":"0"},"db":true}
+{"ts":1778710377411,"counts":{"designers":"6","designers_approved":"5","founding":"3","unclaimed":"4","patterns":"24","patterns_approved":"24","patterns_pending":"0","collections":"8","commission_entries":"6","pending_commission":"545.60","paid_commission":"0","open_inquiries":"9","open_takedowns":"0"},"db":true}
diff --git a/public/marketplace/designers.html b/public/marketplace/designers.html
index c7be387..f5c0632 100644
--- a/public/marketplace/designers.html
+++ b/public/marketplace/designers.html
@@ -10,6 +10,8 @@
.controls { display:flex; gap: 10px; flex-wrap:wrap; margin: 14px 0 22px; align-items:center; }
.controls select { padding: 8px 12px; border-radius: 999px; border: 1px solid var(--mp-border); background: rgba(255,255,255,.8); font-size: 14px; }
.controls input[type=range] { flex: 0 0 200px; }
+ .mp-card .loc { font-size: 12px; color: var(--mp-muted); display:inline-flex; align-items:center; gap:4px; }
+ .mp-card .loc::before { content: '📍'; font-size: 11px; }
</style>
</head>
<body>
@@ -24,6 +26,11 @@
<option value="featured">Featured first</option>
<option value="points">Highest points</option>
<option value="newest">Newest</option>
+ <option value="location">Location (A→Z)</option>
+ </select>
+ <span class="sub" style="margin-left: 14px">Location</span>
+ <select id="locFilter">
+ <option value="">All locations</option>
</select>
<span class="sub" style="margin-left: 14px">Density</span>
<input type="range" id="density" min="180" max="380" value="280" step="10"/>
@@ -34,12 +41,16 @@
<script>
const grid = document.getElementById('grid');
const sortSel = document.getElementById('sort');
+const locFilter = document.getElementById('locFilter');
const density = document.getElementById('density');
-// persist
const KEY = 'wallco.marketplace.designers';
const saved = JSON.parse(localStorage.getItem(KEY) || '{}');
if (saved.sort) sortSel.value = saved.sort;
+if (saved.location) {
+ // Will set after locations load; cached for now.
+ locFilter.dataset.pending = saved.location;
+}
if (saved.density) density.value = saved.density;
applyDensity();
@@ -47,18 +58,53 @@ function applyDensity() {
grid.style.gridTemplateColumns = `repeat(auto-fill, minmax(${density.value}px, 1fr))`;
}
+function persist() {
+ localStorage.setItem(KEY, JSON.stringify({
+ sort: sortSel.value,
+ location: locFilter.value,
+ density: density.value,
+ }));
+}
+
+async function loadLocations() {
+ let j;
+ try { j = await fetch('/api/marketplace/designers/locations').then(r => r.json()); }
+ catch (_) { return; }
+ const locs = j.locations || [];
+ locs.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = loc.location;
+ opt.textContent = `${loc.location} (${loc.designer_count})`;
+ locFilter.appendChild(opt);
+ });
+ if (locFilter.dataset.pending) {
+ locFilter.value = locFilter.dataset.pending;
+ delete locFilter.dataset.pending;
+ }
+}
+
async function load() {
const sort = sortSel.value;
- localStorage.setItem(KEY, JSON.stringify({ sort, density: density.value }));
- const j = await fetch(`/api/marketplace/designers?sort=${sort}&limit=120`).then(r => r.json());
+ const loc = locFilter.value;
+ persist();
+ const params = new URLSearchParams({ sort, limit: '120' });
+ if (loc) params.set('location', loc);
+ const j = await fetch('/api/marketplace/designers?' + params.toString()).then(r => r.json());
grid.innerHTML = '';
- (j.designers || []).forEach(d => {
+ const rows = j.designers || [];
+ if (!rows.length) {
+ grid.innerHTML = '<div class="sub" style="padding:24px;grid-column:1/-1">No designers match this filter yet.</div>';
+ return;
+ }
+ rows.forEach(d => {
const a = document.createElement('a'); a.className='mp-card'; a.href=`/designers/${d.slug}`;
+ const locPill = d.location ? `<span class="loc">${d.location}</span>` : '';
a.innerHTML = `
<div class="img" style="background: linear-gradient(135deg,#eee,#ddd);"></div>
<div class="body">
<div class="title">${d.display_name}</div>
- <div class="sub">${d.studio_name || ''} · ${(d.style_tags||[]).slice(0,2).join(' · ')}</div>
+ <div class="sub">${d.studio_name || ''}${d.studio_name && (d.style_tags||[]).length ? ' · ' : ''}${(d.style_tags||[]).slice(0,2).join(' · ')}</div>
+ ${locPill}
<div class="row">
<span class="mp-pill ${d.is_founding ? 'gold' : ''}">${d.level}</span>
${d.is_verified ? '<span class="mp-pill verified">Verified</span>' : ''}
@@ -70,8 +116,13 @@ async function load() {
}
sortSel.addEventListener('change', load);
-density.addEventListener('input', () => { applyDensity(); localStorage.setItem(KEY, JSON.stringify({ sort: sortSel.value, density: density.value })); });
-load();
+locFilter.addEventListener('change', load);
+density.addEventListener('input', () => { applyDensity(); persist(); });
+
+(async () => {
+ await loadLocations();
+ load();
+})();
</script>
</body>
</html>
diff --git a/src/marketplace/index.js b/src/marketplace/index.js
index 070f46f..db84026 100644
--- a/src/marketplace/index.js
+++ b/src/marketplace/index.js
@@ -277,14 +277,45 @@ function mount(app) {
newest: 'created_at DESC',
points: 'points DESC',
featured: 'is_founding DESC, points DESC',
+ // Location: alphabetical, with locationless designers at the end. Secondary sort by name
+ // so the order is stable within a city.
+ location: "location ASC NULLS LAST, display_name ASC",
}[req.query.sort] || 'points DESC');
+
+ // Optional ?location=Brooklyn filter — case-insensitive substring.
+ const where = ["status='approved'"];
+ const params = [];
+ const locFilter = (req.query.location || '').toString().trim();
+ if (locFilter) {
+ params.push('%' + locFilter + '%');
+ where.push(`location ILIKE $${params.length}`);
+ }
+ params.push(limit); const limIdx = params.length;
+ params.push(offset); const offIdx = params.length;
+
+ const rows = await many(
+ `SELECT id, display_name, slug, studio_name, avatar_url, cover_url, level, points,
+ is_verified, is_founding, is_claimed, style_tags, location
+ FROM mp_designer_profiles
+ WHERE ${where.join(' AND ')}
+ ORDER BY ${sort}
+ LIMIT $${limIdx} OFFSET $${offIdx}`,
+ params
+ );
+ res.json({ designers: rows, limit, offset, sort: req.query.sort || 'points', location: locFilter || null });
+ });
+
+ // ── distinct locations (public; powers the designers-page location filter)
+ app.get('/api/marketplace/designers/locations', async (_req, res) => {
const rows = await many(
- `SELECT id, display_name, slug, studio_name, avatar_url, cover_url, level, points, is_verified, is_founding, is_claimed, style_tags
- FROM mp_designer_profiles WHERE status='approved'
- ORDER BY ${sort} LIMIT $1 OFFSET $2`,
- [limit, offset]
+ `SELECT location, COUNT(*)::int AS designer_count
+ FROM mp_designer_profiles
+ WHERE status='approved' AND location IS NOT NULL AND location <> ''
+ GROUP BY location
+ ORDER BY designer_count DESC, location ASC
+ LIMIT 200`
);
- res.json({ designers: rows, limit, offset });
+ res.json({ locations: rows });
});
// ── designer detail (public)
← d6036b8 marketplace: tests for /api/marketplace/textures (9/9 pass l
·
back to Wallco Ai
·
nav: surface Marketplace · Designers · Patterns in wallco.ai 69fa84a →