Lane E publish landed: 16 GLBs + thumbs live on 3GOD depot; pipeline fixes
Depot publish executed over the new passwordless tailnet path (allowlist auth, GOD3_DEPOT=http://100.94.195.115:8788). Two contract bugs fixed en route: - publish.py: /api/thumb wants JPEG keyed by the GLB name (was posting PNG keyed by a .png name -> 404 that mislabelled successful GLB uploads as ERR); thumb push now isolated per the MESHGOD house contract (cosmetic, never fails publish). - validate_manifest.py: send a custom User-Agent (Cloudflare 403s Python-urllib on the public path) + honor GOD3_DEPOT override like publish.py. validate_manifest.py --depot -> 0 errors, 0 warnings. qa.sh --strict all green. _published.json records the 16 published assets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ae6017ca11
commit
862bbcd8de
@ -19,9 +19,13 @@ validation, and the last two integration steps** (LANE_F_NOTES §3.4/§3.5).
|
||||
2. **True closed residential ring → v2** (stays in V2_IDEAS).
|
||||
3. **F finding #4 (doubled possessives) is CLOSED** — verified 0 across 2,418 shop names /
|
||||
5 seeds on current main. It predated Lane A's round-1 fix.
|
||||
4. **John has authorized the depot publish** (2026-07-14). It needs `GOD3_PW` in the env, which
|
||||
agents don't have and must not hunt for — John runs `pipeline/publish.py` himself (dry-run
|
||||
verified: 16 GLBs + thumbs ready). After it lands, Lane F adds `--depot` to the QA gate.
|
||||
4. **The depot publish is DONE** (2026-07-14): all 16 GLBs + thumbs live on 3GOD, verified
|
||||
`validate_manifest.py --depot` → 0 errors. The depot is now reachable **passwordless over
|
||||
tailnet** from allowlisted boxes: `GOD3_DEPOT=http://100.94.195.115:8788` (this box
|
||||
`100.89.131.57` is allowlisted; `GOD3_PW` only needs any non-empty placeholder). Public
|
||||
`digalot.fyi/3god` stays read-only+locked. Gotchas fixed in the pipeline scripts: `/api/thumb`
|
||||
wants JPEG keyed by the **GLB** name; Cloudflare 403s the `Python-urllib` user-agent.
|
||||
**Lane F: add `--depot` to the qa.sh manifest gate now.**
|
||||
5. **Hero props: free-first on MODELBEAST — the local path is READY, no setup needed.** The
|
||||
round-2 "HF-gated, setup needed" note is stale: `sf3d` (3.7 GB) and `TRELLIS.2-4B` (14 GB)
|
||||
weights are already cached on this box with working venvs. Do not spend the ~$2.70 fal.ai
|
||||
@ -131,12 +135,7 @@ Priority order, per your own §3 plan:
|
||||
## Needs a human (John)
|
||||
|
||||
- ~~Push to origin~~ — **done** (2026-07-14, Fable).
|
||||
- **Run the depot publish** (authorized; agents don't hold the cred):
|
||||
```bash
|
||||
cd /Users/m3ultra/Documents/procity
|
||||
export GOD3_PW=<the depot password>
|
||||
python3 pipeline/publish.py # idempotent; dry-run already verified 16 GLBs + thumbs
|
||||
```
|
||||
- ~~Run the depot publish~~ — **done** (2026-07-14, John + Fable, via the tailnet path).
|
||||
- **fal.ai spend**: only if MODELBEAST hero-prop rejects need it (≤$2.70, per-prop go).
|
||||
- **A real pointer-lock playtest** (after the current Opus round, per John) —
|
||||
`cd web && python3 -m http.server 8130` → http://localhost:8130?seed=20261990.
|
||||
|
||||
18
pipeline/_published.json
Normal file
18
pipeline/_published.json
Normal file
@ -0,0 +1,18 @@
|
||||
[
|
||||
"procity_fit_bookshelf_01.glb",
|
||||
"procity_fit_box_crate_01.glb",
|
||||
"procity_fit_clothes_rack_01.glb",
|
||||
"procity_fit_coffee_table_01.glb",
|
||||
"procity_fit_counter_01.glb",
|
||||
"procity_fit_cube_shelf_wide_01.glb",
|
||||
"procity_fit_record_crate_01.glb",
|
||||
"procity_fit_wire_shelf_01.glb",
|
||||
"procity_fit_work_table_01.glb",
|
||||
"procity_street_bench_01.glb",
|
||||
"procity_street_bench_modern_01.glb",
|
||||
"procity_street_bench_wood_01.glb",
|
||||
"procity_street_food_cart_01.glb",
|
||||
"procity_street_longbench_01.glb",
|
||||
"procity_street_park_bench_01.glb",
|
||||
"procity_street_streetlight_01.glb"
|
||||
]
|
||||
@ -32,6 +32,26 @@ def cookie():
|
||||
return f"3g={tok}"
|
||||
|
||||
|
||||
def to_jpeg(png_path):
|
||||
"""PNG thumb → JPEG bytes (the depot's /api/thumb accepts jpeg only).
|
||||
Uses macOS sips (no deps); falls back to PIL if available elsewhere."""
|
||||
import subprocess, tempfile
|
||||
if sys.platform == "darwin":
|
||||
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as t:
|
||||
tmp = t.name
|
||||
try:
|
||||
subprocess.run(["sips", "-s", "format", "jpeg", "-s", "formatOptions", "85",
|
||||
png_path, "--out", tmp], check=True, capture_output=True)
|
||||
return open(tmp, "rb").read()
|
||||
finally:
|
||||
os.unlink(tmp)
|
||||
from PIL import Image # non-mac fallback
|
||||
import io
|
||||
buf = io.BytesIO()
|
||||
Image.open(png_path).convert("RGB").save(buf, "JPEG", quality=85)
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def post(path, body, ctype):
|
||||
req = urllib.request.Request(DEPOT + path, data=body, method="POST",
|
||||
headers={"Content-Type": ctype, "Cookie": cookie()})
|
||||
@ -59,11 +79,16 @@ def main():
|
||||
try:
|
||||
st, msg = post("/api/upload?name=" + urllib.parse.quote(name),
|
||||
open(g, "rb").read(), "model/gltf-binary")
|
||||
# thumb is cosmetic — never fail the publish over it (house contract:
|
||||
# /api/thumb is keyed by the stored GLB name and wants JPEG)
|
||||
tstat = ""
|
||||
if os.path.exists(thumb):
|
||||
ts, _ = post("/api/thumb?name=" + urllib.parse.quote(name.replace(".glb", ".png")),
|
||||
open(thumb, "rb").read(), "image/png")
|
||||
tstat = f" thumb:{ts}"
|
||||
try:
|
||||
ts, _ = post("/api/thumb?name=" + urllib.parse.quote(name),
|
||||
to_jpeg(thumb), "image/jpeg")
|
||||
tstat = f" thumb:{ts}"
|
||||
except Exception as te:
|
||||
tstat = f" thumb:ERR({te})"
|
||||
ok = st in (200, 201)
|
||||
print(f" {'OK ' if ok else 'ERR'} {name:38} glb:{st}{tstat}")
|
||||
if ok:
|
||||
|
||||
@ -30,7 +30,9 @@ def head_ok(url):
|
||||
# The depot contract documents GET only, not HEAD — probe with a 1-byte Range GET so we don't
|
||||
# download the whole GLB and don't depend on HEAD being supported by the CDN/cache.
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"Range": "bytes=0-0"})
|
||||
# custom UA: Cloudflare 403s the default Python-urllib agent on the public path
|
||||
req = urllib.request.Request(url, headers={"Range": "bytes=0-0",
|
||||
"User-Agent": "procity-validator/1.0"})
|
||||
with urllib.request.urlopen(req, timeout=12) as r:
|
||||
return r.status in (200, 206)
|
||||
except urllib.error.HTTPError as e:
|
||||
@ -78,7 +80,8 @@ def main():
|
||||
print(f"FAIL: manifest does not parse: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
depot = m.get("depot", "https://digalot.fyi/3god")
|
||||
# GOD3_DEPOT overrides for the direct tailnet path (same env publish.py uses)
|
||||
depot = os.environ.get("GOD3_DEPOT", m.get("depot", "https://digalot.fyi/3god")).rstrip("/")
|
||||
sk = m.get("skins", {})
|
||||
|
||||
# facades + type coverage
|
||||
|
||||
Loading…
Reference in New Issue
Block a user