Repository navigation
Conversation
Both namespaces hand a resource something it must return, and both fail in a way that looks like a crash from the player's chair. The cameras guide opens its release section with "a stuck camera is indistinguishable from a crash", and the screen slot is single and shared with the server relay. The platform already releases the view on stop, reload, an uncaught coroutine error, death, an unreadable body, world exit, shutdown, destroy and supersede. Every one of those fires on a generation that has already failed or gone away. A LIVE caller is covered by none of them: a pcall-caught error, an early return down an untested branch, a panel closed by the one path that forgets. `Camera.Shot(id, options, body)` closes exactly that gap -- take, await the blend, run the body under pcall, hand the view back on every exit, and await the returning blend. When it returns the player has their own eyes back, on them rather than mid-blend. A raise becomes `shot_raised` rather than an unwind, because propagating it after the restore is the one ordering that loses the error and keeps the symptom. A rejected take means the shot never happened, so the body does not run -- it would draw over a view somebody else now owns. `Screen.Black` is the same scope for the image. The honest boundary is in the header: a body that never returns is never left, and there the platform's own rules are the only backstop. Seven natives answer a promise as a SECOND value, which `Native.Call` cannot carry -- it keeps the first. They are reached directly, and the promise rides on the Result under a name that says what it settles on: `.blend`, `.covered`, `.restored`, `.finished`. A cut carries none, and `Await(nil)` answers Ok so no call site needs the `if`. Refusals name what the platform leaves you asking: `camera_held_by:` is lifted onto `.holder` with no extra call, a bare `camera_held` spends one read to answer the same question, and `camera_budget_exhausted` names 16 and 64. One source disagreement is recorded rather than resolved: the reference and the devkit's own example read `cameras().held` two incompatible ways. `State` therefore decides on the holder NAME, which is correct under either. 547 checks, 0 failed. All fourteen new blocks mutation-tested. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
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.
Two modules that together cover "something other than the player owns the view".
Both hand a resource something it must return, and both fail in a way that looks like a
crash from the player's chair.
The release guarantee, which is the point
The platform already releases the view on resource stop, reload, an uncaught coroutine
error, death, an unreadable body, world exit, shutdown, destroy and supersede. Every
one of those fires on a generation that has already failed or gone away. A live
caller is covered by none of them: a
pcall-caught error, an earlyreturndown anuntested branch, a panel closed by the one path that forgets the hand-back.
Camera.Shot(id, options, body)closes exactly that gap — take, await the blend, runthe body under
pcall, hand the view back on every exit, await the returning blend.When it returns, the player has their own eyes back, on them rather than mid-blend.
A raise becomes
shot_raisedrather than an unwind, because propagating it after therestore is the one ordering that loses the error and keeps the symptom. A rejected take
means the shot never happened, so the body does not run — it would draw over a view
somebody else now owns.
Screen.Blackis the same scope for the image.The honest boundary is written into the header: a body that never returns is never left
by any path, and there the platform's own rules are the only backstop.
The promise that arrives second
Seven natives answer a promise as a second value, which
Native.Callcannot carry —it keeps the first. They are reached directly, and the promise rides on the Result under
a name that says what it settles on:
.blend,.covered,.restored,.finished. Acut carries none, and
Await(nil)answers Ok so no call site needs theif.Refusals that answer the question they leave you with
camera_held_by:<resource>is lifted onto.holderwith no extra native call; a barecamera_heldspends onecameras()read to answer the same question; if the build willsay neither, the detail says that rather than inventing a holder.
camera_budget_exhaustednames 16 and 64.screen_busysays the slot is shared withthe server relay and points at
isFaded().One source disagreement, recorded rather than resolved
The reference and the devkit's own example read
cameras().heldtwo incompatible ways —"true only for you" versus "true for anybody". They cannot both be right and there is no
game here to settle it.
Statetherefore decides on the holder name, comparedagainst our own, which is correct under either reading;
heldis consulted only whenthere is no name, and answers a deliberately vague
'held'.Verification
375 → 547 checks, 0 failed. Every one of the fourteen new blocks was mutation-tested
— the source broken once per block, the block confirmed red, then reverted. All six CI
gates run by hand.
Every native verified against the devkit at op77.76; nothing unverified. But the live
server runs 82+, so this is "verified at 76", not "verified live" — and no promise
rejection token, no
camera_not_activetolerance and nofadeInrefusal has ever beenobserved in a game.
🤖 Generated with Claude Code