Repository navigation
Develop - #28
Merged
Merged
Develop#28
Conversation
…pod bootstrap clones master - .claude/AGENTS.md: eight agents in four waves plus the conditional LiDAR feasibility track; maintainer decisions of 2026-09-11 recorded at the top. - deploy/runpod/bootstrap.sh and README.md: default clone branch was the now-merged fix/phase3-pose-and-eval; it is master from here on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
The job API (src/api/) parses multipart uploads and streams archives to disk; both need packages the serve extra does not carry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
Maintainer decision 3: prove whether the recon extra (open3d, cv2, trimesh, scipy, skimage, pymeshfix) imports on a plain Ubuntu runner with libgl1 + libglib2.0-0, and what the full pytest suite does there, BEFORE ci.yml is written. Temporary workflow; findings go to docs/ci-probe.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
New src/telemetry/ package (stdlib + the existing jsonschema dependency): configure_logging() with a text formatter that keeps the old console look and a JSON-lines formatter selected by SOBA_LOG_JSON=1; stage_timer() context manager; RunMetrics recording stage durations, gate decisions (verbatim gate_object output, never recomputed), drops and remote calls, written as run_metrics.json and validated against src/telemetry/run_metrics.schema.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
- jobs/store.py: JobRecord + guarded state machine (queued -> running:<stage> -> done|failed, history appended per transition); MemoryJobStore and SqliteJobStore (stdlib sqlite3, WAL, per-call connections). - jobs/queue.py: three-method JobQueue contract (put/get/close) documented for a Redis drop-in; InProcessQueue. - jobs/archive.py: validate_and_extract(), the single extension point for upload hardening: magic-byte sniff (zip/tar.gz/tar), zip-slip and link/device guards, size + member caps, bundle vs TUM layout detection with PerceptionBundle.open; TUM only when an imaging backend exists. - jobs/worker_local.py: LocalWorker thread; modes mock (default, copies a fixture scene or writes a schema-valid empty one), real and gate-only (run_assemble.py subprocess, log under the job dir). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
Module-level `remote_call_hook(endpoint, seconds)`, called in a finally around the urlopen so telemetry can account RunPod calls on success and failure. No signature or return-value change; the runpod-orchestration agent keeps ownership of the class. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
Open3D 0.19.0 imports on a plain ubuntu-latest runner with libgl1 + libglib2.0-0; full suite 238 passed, 2 failed, 8 skipped. Both failures are environment: torch + the dust3r checkout (test_slam mast3r test) and the anthropic SDK (vlm_claude backend test); none of those is in an extra. Skips: 7x gitignored out/scene_office_3, 1x plyfile. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…renced with the pod template Undotted name because .claude/settings.json denies tool access to .env.* (which includes .env.example); rename is a maintainer decision. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…rmat) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…ncy audit 2026-09-11 gitleaks (122 commits + tree), pip-audit (venv + pyproject export) and npm audit (prod + all): all clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…ndings Unused imports/variables removed (no behaviour change; the two dead assignments were pure reads), two bare `import open3d` guards in tests made explicit as pytest.importorskip, and one F821 false positive in slam.py (closure over the enclosing function's `out`) marked noqa. Style rules stay off so the lint job never blocks on formatting. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…, non-root, HEALTHCHECK /scene.json) Editable install + PYTHONPATH=/app/src so src/server.py keeps finding frontend/dist next to it (server.py is a top-level module, not a package). SOBA_EXTRAS build arg switches to serve,api once the api extra lands. No separate frontend container (maintainer decision 2). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…; worker + serverless entrypoints) [recon,serve,dev] plus coacd and runpod, which no extra provides; the Open3D CUDA tensor check warns at build time (CI has no GPU) and is re-runnable for real with `docker run --gpus all soba-pipeline check`. Weights and model checkouts are mounted under /workspace, never baked. Model-pin summary in the header; docs/model-pins.md is the table. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…iff against the committed bundle CI guard for CLAUDE.md invariants 5/6: any byte difference fails the build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…kefile (build/up/test/lint/loadtest) The worker command is a placeholder until feat/job-api and feat/runpod-orchestration land; make loadtest exits 2 until feat/load-testing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
….json telemetry.configure_logging() replaces basicConfig; the body moves into _run(args, metrics) and main() wraps it in try/except/finally so <out>/run_metrics.json is written on success, --gate-only and failure (run.status = failed + error). Stages timed: run, bundle_open, tier_params, engine_init, gate_cache_key, observed_cloud, gate, generation, generate_object, tsdf_fuse, assemble, eval. One gate event per routed object carrying exactly what confidence.gate_object returned (tier 1: None metrics) plus routed (forced strategy) and cached (gate-cache hit). Drops recorded: cloud_too_small, generation_rejected_cached, engine_declined, empty_tsdf_mesh, nothing_to_assemble. RunPod wall time flows through generative.remote_call_hook. All 16 prints are now log.info with the same text; CLI flags and exit codes unchanged. Smoke test stubs open3d in sys.modules, loads the script and fakes the pipeline calls, so the instrumentation is verified on this GPU-less box. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
… builds, frontend-check); probe workflow removed Per docs/ci-probe.md: Open3D imports on ubuntu-latest, so pytest runs the full suite there; the single test that imports torch + the dust3r checkout is marked `gpu` and tests/conftest.py skips it when SOBA_SKIP_GPU_TESTS=1 (only CI sets it). anthropic is installed so the VLM backend test runs. The API image is started and must reach healthy on /scene.json as a non-root uid; the pipeline image is built on a CUDA base after freeing runner disk and its entrypoint modes are exercised without a GPU. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…ecorded stage_timer around assemble (outer), ground, vlm_infer, each _assemble_object (id/track/class/strategy), the engine completion call, CoACD decompose and deoverlap; one `object` event per assembled object and a `scene_written` event. The empty-input filter and the 12-object cap now record `empty_input` / `over_cap` drops on the active run instead of vanishing silently. No behaviour change; assemble() keeps its signature. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
No `telemetry` extra: the package needs only the stdlib and the jsonschema dependency the contract layer already has. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
docs/log/2026-09-11-observability-a.md records the design, the run_metrics.json contract (schema 1) with an example, the gate-recording rule, and what only the GPU machine can verify (with the pod command). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…server.py is a thin shim
src/api/app.py composes three route groups in a fixed order: the legacy
root scene routes (byte-for-byte the Phase-10 handlers), the job API
(POST/GET /api/jobs, GET/DELETE /api/jobs/{id}) and the same scene routes
again under /jobs/{id}/ resolved through the job store, then the static
mount. Later agents add a module and one registration line here.
routes/scene.py parameterises the old handlers by a resolve(request)
callable and, for job routes, a watch callable so /jobs/{id}/events also
emits job_status events and announces objects as the worker writes them.
An explicit /jobs/{id} -> /jobs/{id}/ redirect is needed because the
static catch-all mount pre-empts Starlette's redirect_slashes.
routes/jobs.py streams the upload to disk (aiofiles), validates and
extracts it in a threadpool so a bad archive is a synchronous 4xx
(400/413/415/422 with one JSON error envelope), then creates the record
and enqueues the id. The in-process worker re-enqueues jobs left
`queued` by a previous process at startup, since the in-process queue is
not persisted.
server.py keeps `create_app`, `app`, `main()` and the `_ID_RE`/`_HULL_RE`
re-exports so tests/test_server.py and scripts/serve.py are unchanged;
main() gains --jobs-dir and --worker-mode.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…d to end 79 CPU-only tests (no open3d, no cv2): synthetic PerceptionBundle archives in zip/tar.gz/tar, upload -> 202 + id, every rejection path (missing field, bad tier, MP4 -> 415, non-bundle/zip-slip/links -> 422, size cap -> 413) and that rejected uploads leave no job dir; worker stages and history (queued -> running:validating -> running:assembling -> done | failed), the running:<stage> status read mid-job, the lifespan worker thread, re-enqueue on restart, real/gate-only modes against a stub run_assemble.py; job-scoped viewer/scene.json/meshes/hulls/eval/events; delete; and that the legacy root routes and server re-exports are untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…epo does not record Finding: no git commit, checkpoint sha256 or HuggingFace revision is pinned for YOLO, SAM2, MASt3R, TripoSG, Hunyuan3D 2.1, PatchComplete, PoinTr or ComPC (all clones --depth 1 of the default branch, all HF downloads main). Recorded: filenames, HF ids, run parameters, library versions from the BENCHMARK header, which section used which model on which machine, and the recovery commands for the 4060 and the pod volume. coacd is required by the benchmark's hull colliders but is in no pyproject extra. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…del-pins.md STATUS.md current-state block gains one line: the frontend bundle is served by the API image; CDN offload is deferred until there is real traffic (maintainer decision 2). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…s under /jobs/<id>/ `sceneUrl()` in viewer/loaders.ts prefixes a scene path with the job prefix taken from the page location (`/jobs/<id>`), or nothing at the root; the four fetch/EventSource/loadAsync call sites in SobaViewer.ts and the eval.json fetch in EvalReportPanel.tsx go through it. Bundle assets stay absolute (/assets/...) so no Vite `base` change. The dev proxy also forwards /api and /jobs. React/Three separation, physics semantics and the window.__soba shape are unchanged; dist/ rebuilt with `npm ci && npm run build` (tsc type-check included). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…T, poll) `--path /jobs/<id>/` opens the viewer at a job URL on the spawned server (which reopens out/jobs/jobs.sqlite); the default path keeps the old behaviour byte for byte. submit_job.py is stdlib-only: zips a bundle dir (or sends an archive as-is), POSTs it to /api/jobs and polls the status to done/failed, printing each transition and the viewer URL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
…en3D wheel finding) + index line Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019K7VMdJGk925chC4GgUhym
Job API: upload a bundle/TUM archive, job status, job-scoped viewer (Wave 0)
…-input-and-pins) into develop Prep A: _v2 bundle sync guide + one-shot benchmark pin recovery script
Log index: kept both sides. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NU5Whvd1GH2wKy8L5QYBiY
…d (prep/key-plumbing-and-docs) into develop Prep B: VLM-key plumbing in the GPU guide; STATUS + runbook §6 reconciled with the pod runs
…must not silently run Poisson)
PatchComplete is the completion band at tiers 2-4 (pipeline.yaml, make_engine
default), i.e. in BENCHMARK §3 tier 2 and §4. Without its checkout/weights the
engine returned None and the assembler repaired with Poisson while the job
reported done; both 2026-09-16 pod runs did exactly that.
- deploy/runpod/setup_patchcomplete.sh (+ SETUP_PATCHCOMPLETE=1 in bootstrap):
clone yuchenrao/PatchComplete, fetch the authors' trained_models.zip, place
multi_res.pt + patch_learning_res_{32,8,4}.pt, record SHA256SUMS, smoke-load.
- telemetry: RunMetrics.record_completion / telemetry.completion ->
run_metrics.json `completion` section (schema updated); /metrics gains
soba_completion_total{method}; GET /api/jobs/{id} gains `run` (status, gate,
completion, drops) from run_metrics.json; OpenAPI Job.run. scene.json is
untouched (SceneObject.source forbids extra fields, invariant 1).
- assembler._record_completion labels each completion-band object
(patchcomplete | poisson_fallback[engine_declined|no_engine] | poisson_local);
SOBA_COMPLETION_STRICT=1 raises on any fallback.
- docs: PatchComplete weights recorded as a permanent, unpinnable limitation
(model-pins, STATUS); run-2 log corrected; guide's tier-2-capable setup.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NU5Whvd1GH2wKy8L5QYBiY
…based pins reconstruction sync_bundles.md: the "copy from the 4060" plan was dead (that pod's volume is gone). Now: Replica v1 download, selective extraction of the 8 rooms' habitat/ files, match-test gate on office_3, render --frames 200, validate with BUNDLE_DIR=bundles/room_2_v2 and SOBA_COMPLETION_STRICT=1. scripts/reconstruct_pins.py: default-branch commit / HF revision as of the date each model is first logged running; printed as a table labelled "reconstructed by date"; MASt3R checkpoint kept as the one exact pin (sha256); PatchComplete weights marked unpinnable. Run here; table pasted into docs/model-pins.md with its reading. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NU5Whvd1GH2wKy8L5QYBiY
… (feat/patchcomplete-tier2) into develop PatchComplete on the pod; completion fallbacks visible in run_metrics, /metrics and the job record
…eplica-and-date-pins) into develop Regenerate _v2 bundles from Replica v1 (no copy source exists); date-based model pins reconstruction
… the gate; completion was Poisson Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NU5Whvd1GH2wKy8L5QYBiY
…(aria2c if present); MASt3R exact pins recorded The TUM server delivered ~350 KB/s on the pod (1.8 GB = over an hour). The script wrote straight to trained_models.zip and treated any non-empty file as complete, so an interrupted run would have unzipped a partial archive. Now: download to .part, resume on every rerun, rename only once the zip opens; use aria2c with 8 ranges when installed (the throttle is per connection). docs/model-pins.md: MASt3R checkpoint sha256 e28f91b4... observed on the pod; the mast3r/dust3r commits cloned there equal the date-reconstructed rows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… (PatchComplete verified)
Run 4 on the pod: PatchComplete ran for 4/4 completion objects under
SOBA_COMPLETION_STRICT=1, the pod suite is 510 green, MASt3R pins observed.
But all 10 generative objects were dropped as "engine_declined": TripoSG's
Python deps had not been reinstalled after the container reset, the engine
swallowed the import error, and the record could not tell "unavailable" from
"rejected" (run_assemble would also have cached it as a rejection).
- LocalGpuEngine.last_decline ("rejected: ..." | "unavailable: ..."), forwarded
by SplitEngine.
- run_assemble: drop reason `generation_unavailable` (never cached) apart from
`engine_declined`; SOBA_GENERATION_STRICT=1 raises.
- bootstrap: TripoSG set up before the slow PatchComplete download.
- docs: run-4 log, STATUS, guide (strict flags; rerun bootstrap after a pod stop).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…chComplete code + weight sha256, libraries) From pins.txt of run 4 (2026-09-17). The three code commits equal the date-reconstructed rows; the PatchComplete weight hashes pin what runs from now on (their identity with the July weights stays unknowable). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… develop Generation that cannot run is visible (generation_unavailable, SOBA_GENERATION_STRICT); run 4 recorded
…ume) under one SOBA_STRICT; TSDF-on-GPU claim retracted
A sweep of every exception handler around an ML/geometry call found four
silent fallbacks on the demo's critical path; all four now record, per object,
which implementation actually ran (run_metrics.json `steps`,
soba_pipeline_step_total{step,method}, run.steps on the job record):
- fusion: `_fuse_and_seal`'s bare except is gone. `fused` (completion_source
engine|poisson, fused_triangles) vs `plain_finalize_fallback` with the
exception text: only the former means the completion reached the final mesh.
- keep-band / seal repair: pymeshfix vs poisson_fallback (+reason, context).
- CoACD: decomp.decompose_info -> coacd (parts=n) vs single_hull_fallback
(not installed | raised | no parts).
- mass volume: mass.finalize_mesh_info -> already_watertight | poisson_repair |
hull_volume_fallback (+reason).
SOBA_STRICT=1 (telemetry.strict; per-kind flags kept) fails the run on any
completion, generation or geometry fallback; strict errors are re-raised
through the old handlers. scene.json is untouched (invariant 1).
Docs: STATUS.md, runbook §6, the guide and the run-1 log said "TSDF runs on
the GPU". Only the CUDA capability was checked; tsdf.fuse defaults to CPU:0
and run_assemble.py passes no device, so fusion executes on CPU. Reworded,
and the scripts' S1 message no longer implies otherwise. The device is not
changed here. The rest of the sweep is deferred (maintainer decision).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tracted into develop Geometry fallbacks visible (fusion, keep repair, CoACD, volume) under one SOBA_STRICT; TSDF-on-GPU claim retracted
…A_STRICT=1 on the pod) LocalGpuEngine.regenerate classified every exception from _run_gen as "unavailable", including "no crop staged for this object" (crop staging found no usable view: mask under min_area_px). Under SOBA_STRICT=1 that failed the whole run on track 94 of office_3 before assembly. It is a verdict on the object: recorded as engine_declined with its reason, cacheable, not strict. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A missing crop is a rejection, not an unavailable model (found by strict mode on the pod)
…les regenerated (measured numbers) Run 6b (strict): 4/4 PatchComplete meshes fused from the engine, pymeshfix seals, 10/10 CoACD colliders, 7 TripoSG objects, no fallback, 880 s. Strict first failed on a misclassified missing crop (fixed in PR #24). The 8 _v2 bundles were regenerated on the pod: match-test 0.0 mm, 12 min for all rooms, 2.5 GB; sync_bundles.md now carries measured sizes/times and --no-same-owner. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
…enerative 2, all fused, no fallback) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
… a live Anthropic key Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
…o fallback; mass plausibility notes) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
…ever use a live Anthropic key Run 8 (VLM physics) recorded; test suite never uses a live Anthropic key
…ail is gpu-marked) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
STATUS: WSL box venv is complete
…-backed inspector scene.json's frozen geometry_source cannot tell kept from completed, so the inspector labelled every PatchComplete-completed object "keep · fused tsdf". The server now serves the scene dir's run_metrics.json (same absent-is-200 contract as eval.json, root and job-scoped, documented in openapi.yaml) and the inspector reads route, coverage and completeness from it; without telemetry it falls back to "measured · tsdf". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
… then the viewer Client-side only. The file is described, never uploaded; the staged screens show the numbers the recorded run wrote to run_metrics.json, advance only when the presenter does, and carry a permanent "staged replay, nothing is computed live" banner. The viewer is not mounted before the reveal; "/" stays the plain viewer. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
…terial/route, reset Viewer gains setAutoOrbit, focusObject (eased), setTints and resetObjects, all behind store actions. Presentation mode scales the rem-based tokens from one root attribute and swaps the panels for a large-type story card that keeps the "recorded run" label and the "8 of 12 objects" curation on screen. serve.py rejects a --scene path that is not a directory. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
…pinned nav, safe centring A fixed 150% root zoom overflowed a laptop at 150% display scaling (1280x670 viewport): cards covered the banner and the Next button left the screen. The scale is now clamp(16px, min(1.5vw, 2.67vh), 32px), the staged screens scroll inside a safely-centred area, and the navigation bar is always visible. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4fWTqvtzrSuC3ZmHemmA6
Conference demo flow (/?demo) + truthful gate label from run_metrics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.