Skip to content

The open authoring profile: GPT-6's freedom on the infra's evidence, materials and gate - #48

Merged
ZheningHuang merged 10 commits into
mainfrom
zhening/gpt6-harness
Sep 11, 2026
Merged

The open authoring profile: GPT-6's freedom on the infra's evidence, materials and gate#48
ZheningHuang merged 10 commits into
mainfrom
zhening/gpt6-harness

Conversation

@ZheningHuang

Copy link
Copy Markdown
Member

Why

We ran GPT-6 (Codex, gpt-6-astra) on Office_room two ways and rendered both from the same 59 capture cameras with the same Cycles setup:

  • GPT-6 alone — the raw scan (frames, depth, point cloud, room.usdz) and a one-page brief, no LiteReality tooling. 37 min, 69 tool calls.
  • GPT-6 on our infra — the current simulation authoring profile over the pipeline's assets.

Our side won clearly on materials (fetch_material's photo-matched PBR: carpet pile, wood grain, acoustic tile, the blind's fabric — GPT-6 alone had permission to fetch and still chose flat procedural colour), on real glazing, on reconstructed/articulated assets and on sim export. GPT-6 alone won on layout and fixtures: it measured desk and shelf heights from the depth maps, triangulated chair positions from silhouettes, built the heater with fins and dials, the AC louvres, sockets at the measured spacing, the coat on its hooks — and rebuilt until its own contact/intersection audit passed. Our scripted profile tells the model what to read, in what order, and what not to touch; none of that measuring was possible with what it was given.

This PR keeps what the infra does well and gives the model what the one-shot run had.

What

--author-profile open — a brief, not a script. It states what a finished room has and how it is judged; "how you work is yours", with two conditions (Room.py compiles after every save; save as you go). Furniture boxes may be corrected when a measurement says so; articulated and reconstructed assets must survive. Defaults to the Codex/gpt-6 harness and to budgets the brief needs (300 steps / 400 turns — the one-shot took 69 calls; our last pass was cut at 56).

Evidence kit + pack (evidence_kit/, agent/evidence_pack.py) — plain-python helpers the model imports in its own scripts, laid out beside the room as evidence/: contact sheets of every photograph, the stitches, the raw capture, and measure.probe_depth / triangulate / height_profile / pcd_slice, rectify.rectify_region, arkit_cameras. One README states the data formats and the Y-up→Z-up convention once. Verified on Office_room: the cloud's floor within 3 cm of RoomPlan's; desk and meeting table both read 0.75 m — the numbers GPT-6 measured by hand.

Validation gate (pipeline/room_qc/validate.py) — every rests_on claim measured against its support's top (+1 cm / −2 cm, over its footprint), floor-standing furniture against the floor, undeclared supports, the real-mesh contact report (python-fcl), and whether each articulated asset still reaches the build as a handle with its parts. Box overlaps are advisory (a chair tucked under a table overlaps its box legitimately). validation.json beside the layout; exit 0/2. The brief tells the model to run it; the author stage builds the room if the session didn't and runs it again, recording the verdict. Current Office_room build: PASS — 0 clashes, 131 declared supports all seated.

Trace fixes the same measurement exposed: the session record carries the model that actually ran (the Office_room gpt-6 session was filed under claude-opus-5, the role's name) and the harness; on Codex the raw rollout is copied beside the trace — the only place that says which images the model was actually shown (28, against 84 filename mentions the trace had counted).

Ported from zhening/gpt-6 (never landed on main): the simulation profile with rests_on/attached_to and the --author-profile/--author-turns flags, the Codex whole-output fix, and the chair-QC gate in reconstruct.

Verification

  • 631 tests pass; the two failures are the pre-existing environment ones (test_stdio_bridge_matches_the_registry — the venv lacks the installed package; test_reconstruct_resolves_python_from_canonical_repo_root — a real scans_uploaded/Office_room shadows the test's path).
  • New tests: the kit on a synthetic capture with known geometry (probe lands on the desk top, triangulation round-trips to 1e-3 m, height profile finds desk + floor, slice draws only the desk); the gate on floating / sunk / undeclared / off-support / tucked-under cases and its exit codes; profile registration, budgets, and that the scripted profiles still format.
  • Smoke-tested end to end on Office_room with a 6-call Claude session: pack built, README read first, trace fields present, build + gate PASS.
  • A full open-profile run on Office_room with gpt-6 is queued for when the Codex quota window reopens; the three-way render comparison (GPT-6 alone / current profile / open) will be posted here.

ZheningHuang and others added 10 commits September 11, 2026 18:53
… its surfaces

The `base` profile stops at shell materials and wall fixtures, and that is what
makes a finished render read as a MODEL of a room rather than a room: every
surface is right and the room is empty. An unoccupied desk is the loudest
statement a scene can make that nobody was ever there. Separately, a room whose
objects do not say what holds them up cannot be simulated no matter how good it
looks — geometry cannot distinguish a mug resting on a desk from a mug floating
a millimetre above one.

`--author-profile simulation` asks for both, in four gated jobs:

  1  shell structure + materials, floor -> walls -> ceiling
  2  LIGHT — the real luminaires from the photographs, geometry AND a Blender
     light, matched on colour temperature, intensity and the direction the
     photo's shadows imply. Before the props, because every material judgement
     after it is made under that light.
  3  the fixtures the scan missed
  4  the small objects, surface by surface, from the raw frames rather than the
     stitches — which flatten exactly the things this job is about. With an
     explicit instruction not to tidy: axis-alignment is the giveaway of a
     generated room, and not to invent a prop it cannot point at in a photo.

Simulation readiness is enforced rather than encouraged. `group_fixture` now
takes `rests_on=` / `attached_to=`, stores them on the handle, and `_register`
carries them out into `room_layout.json` where a simulator reads them. The brief
requires one or the other on every added object, and requires the claim to be
TRUE — the underside on the surface, read from the geometry rather than placed
by eye, because a prop that floats falls on the first simulated frame and one
that intersects gets fired across the room.

The authoring profile, step budget and turn cap had no CLI spelling at all; they
do now (`--author-profile`, `--author-steps`, `--author-turns`).

Verified against Office_room with gpt-6-astra: 143 objects, 132 support
declarations (the other 11 are the floor, ceiling and nine walls, which
correctly have none), all 47 `rests_on` claims seated within 2 cm — none
floating, none sunk — and an FCL mesh check over 131 bodies finding 2 genuine
clashes, both a continuous skirting/trunking run drawn through a door opening.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sip8HKWXRgZhZpzsBnk5Uf
One `codex exec --json` event is one line, and an event carries the entire
output of the command it reports — a `cat Room.py` on an authored room is over
100 KB, a stitch listing more. asyncio's StreamReader caps a line at 64 KiB and
raises `ValueError: Separator is found, but chunk is longer than limit`, which
killed the session a couple of tool calls in AND reported success, so the run
looked like a model that had simply stopped early.

The cap has to clear the largest event a session can emit, not the typical one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sip8HKWXRgZhZpzsBnk5Uf
… PBR

Two defaults that were quietly costing every room.

`--profile` defaulted to `base`, which stops after shell, materials and wall
fixtures. That is why rooms came back with three empty tables and no light
datablocks: the brief never asked for the props or the luminaires. `simulation`
is the brief that produces a room worth judging — lights that actually emit,
objects on the surfaces, and every added object grouped with `rests_on` /
`attached_to` so a physics engine can read it. Make it the default; `base` is
still there for a surfaces-only pass.

`fetch_material` was described accurately and then ignored. The prompt listed
the procedural `make(...)` helper as a peer alternative, and the agent read that
as permission to write flat RGB and move on — a room whose surfaces are all flat
colours is the single biggest remaining gap between the render and the photo.
The tool now reads as an instruction: every surface with visible texture gets a
fetched PBR set, only genuinely flat modern paint may stay a plain colour, and
that has to be justifiable from the photograph. Procedural `make(...)` is
demoted to what it is good at — procedural weave and grain.
`mesh_qc.chair_repair` is the only thing in the pipeline that looks at what
TRELLIS produced rather than at what it was asked for: it flags fused floor
slabs, squat blobs and floating fragments, then repairs a bad cluster by
regenerating its reference image and re-running TRELLIS.

The reconstruct stage has always forwarded `--chair-qc` to `scene_init.flow`,
and `flow` has always honoured it — but nothing on the CLI side ever put
`chair_qc` into the options dict, so the flag was unreachable and every chair
went into every room ungated.

It is ON by default. The checks are local geometry and cost nothing; only a
cluster that FAILS pays for a regeneration. `--no-chair-qc` opts out, and then
a bad chair ships as-is.
A one-shot GPT-6 run given nothing but the raw scan and a page of intent got
the desk at 0.75 m, the shelves at 1.42 and 1.84 m and the meeting chairs where
the photos put them — by probing the depth maps, slicing the point cloud by
height and triangulating pixels across frames, all in a numpy script of its
own. Our authoring pass hands the model head-on stitches and RoomPlan boxes and
no way to ask the scan a number; it has to guess dimensions the capture already
knows.

The kit is those helpers as FILES, not tools: `read_scan` (frames, poses,
intrinsics, depth in metres, confidence, the cloud, all in Blender Z-up),
`measure` (probe_depth, triangulate, height_profile, pcd_slice), `rectify`
(a screen or board lifted out of a photo as a texture), `contact_sheets`
(every photograph, upright, before anything is built) and `arkit_cameras`
(the capture cameras inside Blender). A tool answers one question the way
its author framed it; a function the model can combine in a script is the
freedom the measured run actually used.

`evidence_pack.build()` lays it out beside the room — `evidence/` with the
scan and stitches linked, the helpers COPIED so a run stays self-describing,
contact sheets prebuilt, and one README stating the data formats and the
Y-up→Z-up convention once. Beside, not inside: the room dir is copied
wholesale downstream and a stray file in it is a trap.

Checked against Office_room: the cloud's floor comes out within 3 cm of
RoomPlan's (LiDAR scatters returns 30 cm under the real floor, so the floor
is the strongest low band, not the lowest point), and with RoomPlan's floor
the height profiles read desk 0.75 m and meeting table 0.75 m — the numbers
the one-shot run measured by hand. Triangulation round-trips a projected
point to 1e-3 m.
…eometry

`rests_on` and `attached_to` made simulation readiness a statement the author
makes; nothing checked whether the statement was true. The one-shot run that
outperformed our pass on layout rebuilt itself until its own audit passed —
floor contact within 1 mm, no pairwise intersections, no open boundary edges.
This is that audit as part of the harness.

`pipeline/room_qc/validate.py` reads the build (room_layout.json, Room.glb,
manifest.json) and reports: every `rests_on` claim measured from the support's
top (+1 cm floating / -2 cm sunk, and over its footprint), floor-standing
furniture against the floor, objects that declare neither support, the
real-mesh contact report from check_collisions when python-fcl is present,
and whether every articulated asset in the manifest still reaches the build
as a handle with its moving/fixed parts. Open-edge counts are reported, not
failed — a reconstructed mesh that is not closed is a decomposition problem.

Axis-aligned overlaps are a review list, never a verdict: a chair tucked under
a table overlaps its box legitimately (77 such pairs in Office_room, 0 mesh
clashes). Exit 0 pass / 2 fail / 1 could-not-run, and `validation.json`
beside the layout so the stage summary, the trace and the model itself read
the same verdict. Lives in pipeline/room_qc because it needs both room_ops
and the collision code under agent/, and room_ops must not import agent.
Measured on Office_room: GPT-6 alone, given the raw scan and a page of intent,
came back in 37 minutes with a room closer to the photographs on layout and
fixtures than our scripted authoring pass — heater with fins and dials, AC
louvres, sockets at the measured spacing, the coat on its hooks — while our
pass kept the clearly better materials (fetch_material's photo-matched PBR),
the real glazing, the reconstructed and articulated assets and the sim export.
The scripted profiles prescribe an order, a cadence and what not to touch
because a step-budgeted model on Claude Code banked cheap calls and edited at
the end; that is a harness problem answered here with gates on the result
instead of rules on the process.

`--author-profile open`:
  * the brief states what a finished room has and how it is judged, not how
    to work — "HOW YOU WORK IS YOURS", with two conditions (Room.py compiles
    after every save; save as you go);
  * the evidence pack is built beside the room and put in the prompt and the
    harness's readable roots: contact sheets to review first, stitches, the
    raw capture, and the measurement helpers to import in the model's own
    scripts; furniture boxes MAY be corrected when a measurement says so;
  * the validation gate is the finish line: the brief tells the model to run
    it, and the author stage builds the room (if the session did not) and
    runs it again afterwards, recording the verdict as an artifact and a
    warning;
  * budgets the brief needs rather than the scripted pass's — 300 steps /
    400 turns (the one-shot took 69 calls; our pass was cut at 56) — and the
    codex/gpt-6 harness by default, unless a provider is set.

Two trace fixes the same measurement exposed: the session record now carries
the model that actually ran (the Office_room gpt-6 session was filed under
claude-opus-5, the role's name) and the harness name; and on Codex the raw
rollout is copied beside the trace, because it is the only place that says
which images the model was really shown — 28, against 84 filename mentions
the normalised trace had counted.

Smoke-tested end to end on Office_room with a 6-call Claude session: pack
built, README read first, trace fields present, build + gate PASS on the
seed room.
scene.json's capture link is the pipeline's input dir, which for Office_room
holds one .usdz; input/rgbd/ is the pipeline's own split layout. The kit reads
the raw frame_NNNNN.* capture, which lives under scene.json's roots.scans.
Resolve it there (or LITEREALITY_SCAN, or the settings scans dir), and refuse
to build a pack with no frames — the first gpt-6 run of the open profile was
handed a pack with a usdz and the stitches and said so within four calls.
@ZheningHuang
ZheningHuang merged commit 878d114 into main Sep 11, 2026
1 check passed
@ZheningHuang

Copy link
Copy Markdown
Member Author

Result: the open profile on Office_room (gpt-6 via Codex)

Ran after merge on a copy of the scene (run-open/). 25.5 min, 65 tool calls, finished on its own (not cut by budget or quota). Gate from the harness: PASS — 128 objects, 115 declared supports, 0 support failures, 0 mesh clashes, articulation intact.

What it did, in order: reviewed all 59 photographs from the contact sheets → built and rendered the baseline → fetched real PBR sets for carpet, timber, ceiling and fabrics (fetch_material) → measured with the kit (desk 0.75 m, shelves 1.42 / 1.81–1.84 m) and found the scan's meeting-table box oversized, refitting it to 1.04 m Ø × 0.75 m and the desk depth to 0.87 m → authored shelves, boards, trunking, sockets, heater, AC, blinds, the coat (following the door), shelf contents and desk clutter with explicit supports → ran the gate (PASS on the first build) → kept fixing against its own photo comparisons (ceiling, one ceiling-grid/light-frame contact) → rebuilt, gate PASS again.

Four-way comparison from the same 59 capture cameras, identical Cycles setup: photo | GPT-6 alone | previous infra (simulation) | GPT-6 + new infra (open). The open column has the previous profile's materials and glazing and the one-shot run's measured layout and fixtures — the right-sized meeting table with the chairs where the photos put them, the heater with fins under the desk, the coat on its hooks, the AC louvres, the real carpet.

Renders and sheets: run/_gpt6_oneshot/compare/{compare4_mandatory.jpg, compare4_extra.jpg, compare4_all59.mp4}; the authored room: run-open/Office_room/realism_authoring/.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant