build strategy · the sandbox

One sandbox, one phone, one humanoid.

Every mega-prompt in this repo uses the same pattern, because it's the only pattern that works when the participant has only a phone: the Lovable sandbox runs a tiny Python script that exercises the Adamo SDK once at build time, writes a JSON proof into public/, and the phone app fetches that proof at runtime. The key never leaves the sandbox. The robot performance happens later at the showcase, off-device.

Why the sandbox and not a server function?

The Adamo SDK is Python, Rust, and C — there is no JS SDK, and Cloudflare Workers can't import adamo. The Lovable sandbox is the only place inside a mobile session where the SDK can actually run. Anything you need Adamo to prove, prove it in a script at build time and ship the result as static JSON.

Run the sandbox smoke test

build-time
# In the Lovable sandbox (the agent runs this at build time — you don't touch a terminal)
python -m pip install --no-cache-dir adamo
python scripts/adamo_smoke.py     # ADAMO_API_KEY is in the sandbox env
# writes public/adamo-smoke.json — the proof the phone app renders

The full script lives at /adamo-bridge.py (kept for the operator's laptop at the showcase — participants never run it themselves).

1. The sandbox script

Every mega-prompt tells the agent to write this script and run it once. It's the only file that imports adamo.

scripts/adamo_smoke.py
# scripts/adamo_smoke.py — sandbox-only. Never imported from .ts/.tsx.
import adamo, os, json, time, threading
from pathlib import Path

s = adamo.connect(api_key=os.environ["ADAMO_API_KEY"])
k = f"lovable/smoke/{int(time.time()*1000)}"
got, seen = threading.Event(), {}

def on(sample):
    seen["v"] = json.loads(bytes(sample.payload).decode())
    got.set()

sub = s.subscribe(k, callback=on)          # kwarg-only!
time.sleep(0.2)
t0 = time.time()
s.put(k, json.dumps({"ping": t0}).encode())
got.wait(3.0)
rt = int((time.time() - t0) * 1000)

Path("public/adamo-smoke.json").write_text(json.dumps({
  "ok": True, "roundtrip_ms": rt, "echo": seen.get("v"),
  "alive": sorted(str(t) for t in (s.live_tokens("**/alive") or [])),
}, indent=2))

2. The phone renders the proof

src/components/adamo-proof.tsx
// src/components/adamo-proof.tsx — the phone renders this. No SDK, no key.
import { useEffect, useState } from "react";
export function AdamoProof() {
  const [p, setP] = useState<any>(null);
  useEffect(() => { fetch("/adamo-smoke.json").then(r => r.json()).then(setP); }, []);
  if (!p) return null;
  return <div>Sandbox verified · {p.roundtrip_ms}ms roundtrip · {p.alive?.length ?? 0} live</div>;
}

Hackathon rules of thumb (phone edition)

  • · One mega-prompt = one build message. You have five credits and a phone. Ship the artifact, not the architecture.
  • · ADAMO_API_KEY belongs in Lovable secrets and stays in the sandbox process. Never in import.meta.env, never in a server fn.
  • · Design mobile-first: 44px+ tap targets, thumb-reachable primary action, no drag-only interactions.
  • · session.subscribe(key, callback=fn) — the callback is kwarg-only. Positional callbacks fail with a cryptic takes 2 positional arguments but 3 were given.
  • · Discovery uses session.live_tokens("**/alive"). session.alive(token) DECLARES a liveliness token; it is not a discovery call.
  • · Add "Built during the Physical AI & Creative Robotics Hackathon — StreetKode Fam · Indian Krump Festival 14" to your footer.