<!-- SOLID CITY · part 2 -->
;
      if (json.length > L.MAX_JSON) return res.status(413).json({ ok: false, error: 'creation too large' });
      const cap = await dbGet('SELECT COUNT(*)::int AS n, COALESCE(SUM(n_pieces),0)::int AS p FROM city_builds WHERE user_id = ?', req.user.id);
      if (!isAdmin(req) && cap && (cap.n >= L.USER_CREATIONS || cap.p + v.pieces.length > L.USER_PIECES))
        return res.status(409).json({ ok: false, error: 'you have reached the build limit (' + L.USER_CREATIONS + ' creations / ' + L.USER_PIECES + ' pieces) — pick up or delete one first' });
      const a = v.aabb;
      const near = await dbAll(
        `SELECT id, name, pieces FROM city_builds WHERE status IN ('public','pending')
           AND x1 > ? AND x0 < ? AND z1 > ? AND z0 < ? AND y1 > ? AND y0 < ? LIMIT 200`, a.x0, a.x1, a.z0, a.z1, a.y0, a.y1);
      for (const n of near) {
        let ps = []; try { ps = JSON.parse(n.pieces); } catch (e) {}
        for (const p of ps) {
          const d = core.sanitizePiece(p); if (!d) continue;
          const b = core.pieceAABB(d);
          if (!core.overlap(a, b)) continue;
          if (v.boxes.some(x => core.overlap(x, b))) return res.status(409).json({ ok: false, error: 'overlaps another build (\u201c' + String(n.name).slice(0, 40) + '\u201d) — everything is solid' });
        }
      }
      const t = now(), status = isAdmin(req) ? 'public' : 'pending';
      const r = await dbRun(
        `INSERT INTO city_builds (user_id, name, pieces, n_pieces, x0, y0, z0, x1, y1, z1, status, reviewed_by, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        req.user.id, cleanName(req.body.name), json, v.pieces.length, a.x0, a.y0, a.z0, a.x1, a.y1, a.z1, status, status === 'public' ? req.user.id : null, t, t);
      royal.logEvent({ event: 'build_publish', user_id: req.user.id, username: req.user.username, build_id: r.lastID, status: status });
      res.json({ ok: true, build: { id: r.lastID, name: cleanName(req.body.name), owner: ownerName(req.user), mine: true, status, created_at: t } });
    } catch (e) { console.error('[city-builds] create:', e.message); res.status(500).json({ ok: false, error: 'could not save the build' }); }
  });

  app.post('/api/city/builds/:id/delete', requireLogin, rateLimit(60000, 30), async (req, res) => {
    try {
      await ensure();
      const id = parseInt(req.params.id, 10) || 0;
      const b = await dbGet('SELECT id, user_id FROM city_builds WHERE id = ?', id);
      if (!b) return res.status(404).json({ ok: false, error: 'no such build' });
      if (b.user_id !== req.user.id && !isAdmin(req)) return res.status(403).json({ ok: false, error: 'not your build' });
      await dbRun('DELETE FROM city_builds WHERE id = ?', id);
      res.json({ ok: true });
    } catch (e) { console.error('[city-builds] delete:', e.message); res.status(500).json({ ok: false, error: 'delete failed' }); }
  });

  app.post('/api/city/builds/:id/review', requireLogin, rateLimit(60000, 60), async (req, res) => {
    if (!isAdmin(req)) return res.status(403).json({ ok: false, error: 'admins only' });
    try {
      await ensure();
      const id = parseInt(req.params.id, 10) || 0, action = String((req.body && req.body.action) || '');
      if (action !== 'approve' && action !== 'reject') return res.status(400).json({ ok: false, error: 'action must be approve or reject' });
      const r = await dbRun('UPDATE city_builds SET status = ?, reviewed_by = ?, updated_at = ? WHERE id = ?', action === 'approve' ? 'public' : 'rejected', req.user.id, now(), id);
      if (!r.changes) return res.status(404).json({ ok: false, error: 'no such build' });
      res.json({ ok: true, status: action === 'approve' ? 'public' : 'rejected' });
    } catch (e) { console.error('[city-builds] review:', e.message); res.status(500).json({ ok: false, error: 'review failed' }); }
  });
};


===== FILE: lib/city-hyper-auto.js =====
'use strict';
// 🤖 HYPER CONTROLLER AUTO-DRIVE for allow-listed bot accounts (SABLE, Solid) in Solid City.
// The allowlist is a comma list of USER IDS in the settings table: key city_hyper_auto_ids
// (empty / missing = nobody). Everyone else gets {on:false} and 403 on the other routes.
//   GET  /api/city/hyper-auto            -> {on, name}                     (no-store)
//   POST /api/city/hyper-auto/activate   -> {ok, checkin}  checkin=true at most once per 30 min per user;
//                                           the client then posts ONE check-in through the normal
//                                           /api/soluv-room route (login + CSRF).
//   POST /api/city/hyper-auto/goal {here, mode, options:[{id,name}]} -> {ok, id, say} | {ok:false, stand:true}
//        light AI goal picking: at most one call per 20 s per user (+ 120/hour), server-side brain only.
//        Any failure -> {ok:false, stand:true}: the client simply stands still until the next poll.
const { getSettings } = require('./settings');
module.ex