754. Lesson identity
755. Scope note
Pointer-lock dialogue is a documented CONTRABAND theme. The quartermaster, supply-transfer, and route-clue branches used below are hypothetical instructional examples. They do not describe a documented CONTRABAND implementation, incident, or game rule.
756. Learning objective
After this lesson, you can specify one narrative branch as a traceable choice contract, separate attempted intent from confirmed outcome, assign every durable consequence to its responsible system, and document safe pointer-lock entry, exit, observation, and fallback behavior.
757. Why this matters
A dialogue choice often touches more than dialogue. It may request a resource transfer, create a relationship consequence, or advance an objective. That does not make the dialogue controller authoritative over inventory, relationships, or quests.
A hidden global write occurs when one handler silently changes state owned by several domains. Such a branch is difficult to validate because selection, authorization, persistence, and presentation are collapsed into one operation. It may also leave inconsistent state: inventory rejects a transfer while a relationship or quest system has already recorded success.
A traceable branch separates two stages:
- Intent: what the player asked to do.
- Confirmed outcome: what the authoritative system validated and committed.
Only the confirmed outcome should authorize downstream consequences that depend on success.
Dialogue also creates an interaction-mode boundary. Pointer lock is controlled through an asynchronous browser lifecycle and is not guaranteed merely because code called requestPointerLock(). Restoration may require transient user activation, and success or failure must be observed rather than assumed.
758. Prior knowledge
You should already be able to:
- distinguish dialogue state from dialogue presentation;
- identify the system responsible for a durable fact;
- describe dialogue nodes and choices;
- apply the Stage 2 distinction between transient presentation and persistent game state;
- recognize pointer lock as an interaction mode rather than a narrative fact.
The previous lesson established that a dialogue panel should not become the authority for durable world state.
759. Core concept: intent is not confirmation
A useful choice contract identifies the choice, its request, its authority, and the possible confirmed results.
| Contract element | Question | Hypothetical example |
|---|---|---|
| Choice identity | What did the player select? | offer_one_crate |
| Intent or request | What operation is being requested? | Transfer one crate from the player |
| Authoritative handler | Which system validates and commits that operation? | Economy or inventory system |
| Confirmed success | What event proves the operation completed? | SuppliesTransferCompleted |
| Confirmed rejection | What event reports that it did not complete? | SuppliesTransferRejected |
| Downstream consumers | Which systems may react to confirmed success? | Relationship and quest systems |
An intent event is not evidence that the requested change happened. Relationship and quest systems must not independently interpret an unvalidated SuppliesTransferRequested event as success. They consume SuppliesTransferCompleted when their consequences require a completed transfer.
760. A safe event sequence
The hypothetical quartermaster branch can be specified as follows:
1. Dialogue records selection:
choiceId = offer_one_crate
2. Dialogue emits an intent:
SuppliesTransferRequested
correlationId = transfer-42
quantity = 1
source = player
recipient = quartermaster
3. Economy system handles the request:
- validate that the request is permitted
- validate that one crate is available
- commit the inventory removal if valid
4a. On successful commit, economy emits:
SuppliesTransferCompleted
correlationId = transfer-42
quantity = 1
4b. On rejection, economy emits:
SuppliesTransferRejected
correlationId = transfer-42
reasonCode = insufficient_supplies or another defined reason
5. Relationship and quest systems:
- ignore the request as proof of success
- consume the completed event when relevant
- do not apply success consequences after a rejected result
The names are examples, but the distinction is required: selection produces a request; the responsible system produces the confirmed result.
761. Correlation, ordering, and no-partial-write rules
A branch specification must state more than event names.
Correlation
Every request and result needs a shared correlation identifier. This lets consumers distinguish two similar selections and associate a completion or rejection with the correct dialogue branch.
Ordering
A success-dependent consequence must not be processed before the authoritative completion event. The required logical order is:
choice selected
→ transfer requested
→ economy validates and commits
→ completed or rejected result
→ success-dependent consumers react to completed result
Delivery order may vary in an asynchronous implementation, so consumers should enforce the dependency rather than relying only on arrival timing.
No partial write on rejection
If the economy system rejects the transfer:
- inventory remains unchanged;
- no relationship reward for a completed transfer is persisted;
- no quest progress that requires a completed transfer is persisted;
- the player receives a rejection presentation rather than a success presentation.
Duplicate delivery and retry
A consumer should treat the confirmed event idempotently using its event or correlation identifier. Receiving the same completion more than once must not apply the same relationship or quest consequence repeatedly.
Cross-domain failure after confirmation
The confirmed transfer proves the economy operation completed; it does not prove that every downstream consumer has already persisted its own consequence. Each consumer records its own processing status and can retry safely. If the design requires all domains to succeed or fail together, the specification needs an explicit coordinator and compensation policy rather than an assumption of one invisible global transaction.
762. Ownership map
| Fact | Responsible system | Allowed trigger |
|---|---|---|
| Inventory crate removed | Economy or inventory | Validated transfer request |
| Transfer completed or rejected | Economy or inventory | Result of validation and commit attempt |
| Relationship consequence recorded | Relationship system | Confirmed transfer completion |
| Objective progress recorded | Quest system | Confirmed transfer completion, if the objective requires it |
| Dialogue feedback displayed | Dialogue presentation | Confirmed completion or rejection result |
| Pointer-lock state observed | Interaction-mode controller | Pointer-lock lifecycle events |
The dialogue layer may display a pending state while validation runs. It must not announce completed success merely because the player selected the option.
763. Pointer lock is an asynchronous interaction contract
Dialogue entry and exit should be specified independently from narrative persistence.
Entry
When pointer-locked gameplay enters dialogue:
- Record the prior interaction mode.
- Release pointer lock.
- Observe the resulting pointer-lock lifecycle change.
- Give focus and input interpretation to the dialogue interface.
- Do not use pointer-lock state as evidence about narrative success or failure.
Exit
When dialogue closes:
- Record the exit cause, such as a player click, cancellation, or an asynchronous narrative result.
- Close the dialogue presentation and restore gameplay input only when gameplay is actually resuming.
- Determine whether the prior gameplay mode required pointer lock.
- Determine whether the target canvas remains eligible.
- Request pointer lock only from a path compatible with browser user-activation requirements.
- Observe
pointerlockchangeto confirm the resulting state. - Handle
pointerlockerroror an unchanged unlocked state as a failed request, not as success.
requestPointerLock() is a request, not a synchronous state assignment. A browser may require transient user activation. If dialogue closes only after asynchronous processing, the activation from the original click may no longer be available.
Safe fallback
If automatic restoration is unavailable or rejected:
- keep the camera-look path disabled while the pointer remains unlocked;
- present an explicit control such as Click to resume or Click to recapture pointer;
- call
requestPointerLock()from that new player action; - continue observing lifecycle events;
- permit non-pointer-lock navigation or cancellation where the design supports it.
Do not repeatedly request pointer lock in a hidden loop, assume success, or leave camera look active while the pointer is free.
764. Combined hypothetical case
Dialogue entry:
priorMode = pointer_locked_gameplay
release pointer lock
wait for pointerlockchange
activate dialogue input
Choice selection:
emit SuppliesTransferRequested(correlationId)
display pending feedback
Economy result:
if committed:
emit SuppliesTransferCompleted(correlationId)
else:
emit SuppliesTransferRejected(correlationId, reasonCode)
Downstream response:
relationship and quest consume only the completed result
each consumer deduplicates by event or correlation ID
rejection produces no success-dependent durable writes
Dialogue exit after asynchronous result:
restore gameplay input mode
do not assume the earlier click still provides user activation
attempt pointer lock only if the current exit action qualifies
confirm through pointerlockchange
on pointerlockerror, show an explicit click-to-resume control
This design separates the narrative result from the interaction result. A rejected pointer-lock request does not reverse a confirmed supply transfer, and a rejected supply transfer does not determine whether the browser can capture the pointer.
765. AI-native workflow
Use AI to inspect a contract, not to invent its authorities or rules.
- Write the choice request, authoritative handler, confirmed outcomes, and consumers.
- Add a correlation identifier and the required ordering.
- State the rejection and duplicate-delivery rules.
- Write pointer-lock entry, exit, lifecycle-event, user-activation, and fallback conditions.
- Ask AI to produce an event trace and flag consumers that react to intent as though it were confirmation.
- Ask it to identify cross-domain writes, missing owners, missing rejection paths, and pointer-lock requests whose success is assumed.
- Review the result yourself before requesting implementation.
A useful prompt is:
“Review this narrative choice and pointer-lock interaction contract. Separate intent from confirmed outcome, trace correlation and ordering, identify every durable write and its responsible system, test rejection and duplicate-delivery behavior, and flag pointer-lock transitions that assume user activation or synchronous success. Do not invent game rules or modify files.”
766. Common mistakes
Fan-out from unvalidated intent
onChoiceSelected():
emit SuppliesTransferRequested
relationship.onRequested(): rewardRelationship()
quest.onRequested(): advanceObjective()
economy.onRequested(): maybeRemoveCrate()
This allows relationship and quest success to persist even when economy rejects the transfer.
Hidden global mutation
onChoiceSelected():
inventory.crates -= 1
relationship.quartermaster += 10
quest.suppliesObjective.complete = true
saveGame()
This hides validation, authority, ordering, and failure behavior inside dialogue code.
Assuming pointer-lock restoration
closeDialogue():
await finishDialogueWork()
canvas.requestPointerLock()
enableCameraLook()
After asynchronous work, transient user activation may be gone. The request can fail, and camera look must not be enabled on the assumption that capture succeeded.
767. Guided practice
Design a hypothetical branch in which the player can share a route clue with a contact or keep the clue private. Do not attribute this scenario to CONTRABAND and do not implement it.
Complete this specification:
| Field | Your specification |
|---|---|
| Choice IDs and displayed text | |
| Intent emitted by each choice | |
| Correlation identifier | |
| System responsible for validating each intent | |
| Confirmed success and rejection events | |
| Required event order | |
| Consumers of confirmed success | |
| Durable fact owned by each consumer | |
| No-partial-write rule on rejection | |
| Duplicate-delivery rule | |
| Player feedback while pending | |
| Player feedback after confirmation or rejection | |
| Prior pointer-lock mode | |
| Dialogue exit cause | |
| User-activation availability at exit | |
| Lifecycle events to observe | |
| Fallback after a rejected pointer-lock request |
Then check:
- Can every success-dependent consumer point to a confirmed event rather than an intent?
- Does the rejection path leave all success-dependent domains unchanged?
- Can duplicate completion delivery occur without duplicate consequences?
- Can a reviewer trace every durable fact to one responsible system?
- Does pointer-lock restoration depend on observed lifecycle state rather than a function call alone?
- If user activation has expired, is there an explicit player-action fallback?
- Are narrative completion and pointer-lock restoration treated as independent outcomes?
Revise the contract if any answer is no.
768. Validation evidence
Your completed branch specification should include:
- at least two choices;
- an intent for each choice;
- an authoritative validator for each operation that can fail;
- confirmed success and rejection results;
- shared correlation data;
- explicit ordering and no-partial-write rules;
- an idempotency or duplicate-delivery rule;
- one responsible system for each durable fact;
- pointer-lock entry and exit conditions;
- the relevant lifecycle observations;
- user-activation analysis;
- an explicit fallback after restoration failure.
The branch is traceable when a reviewer can determine what was requested, what was confirmed, which system persisted each consequence, and how rejection avoids false success. The interaction transition is safe when gameplay does not assume pointer capture and the player has a clear recovery action.
769. Key takeaways
- A selected choice expresses intent; it does not necessarily prove completion.
- The authoritative system emits the confirmed success or rejection result.
- Success-dependent systems consume confirmation, not unvalidated intent.
- Correlation, ordering, rejection, and idempotency rules prevent inconsistent consequences.
requestPointerLock()is asynchronous and may require transient user activation.- Pointer-lock success must be observed through lifecycle state, with an explicit fallback when restoration fails.
- Narrative persistence and interaction-mode restoration are separate outcomes.
770. Next lesson
Continue to 2.9 — Faction systems. The confirmed relationship consequence identified here becomes an input to a faction-owned relationship contract. Dialogue remains the source of the choice or confirmed outcome, while the faction relationship system defines and owns the durable relationship rule.
771. Knowledge check
Answer these items for yourself before reading the answers.
A player selects “Offer one crate.” What should the dialogue system communicate first when the economy system must validate the transfer?
Show answer and feedback
Answer: A transfer request or intent with correlation data
Why: Selection expresses intent. The economy system must validate and commit the transfer before emitting a confirmed completion or rejection.
Which sequence prevents relationship and quest systems from recording success after an invalid transfer?
Show answer and feedback
Answer: Dialogue emits a correlated request; economy validates and commits; downstream systems consume only the confirmed completion
Why: Success-dependent consumers need the authoritative completion event. A request alone does not prove that the transfer was valid or committed.
The economy system rejects a transfer because the required item is unavailable. Which rules belong in the branch contract?
Show answer and feedback
Answer: Inventory remains unchanged; Success-dependent relationship and quest writes do not occur; The dialogue presents a rejection result associated with the original correlation identifier
Why: A rejection must not leave success-dependent partial state. Correlation allows the rejection feedback to be matched to the original choice.
What proves that a pointer-lock restoration request succeeded?
Show answer and feedback
Answer: The observed pointer-lock lifecycle state confirms that the intended element owns the lock
Why: requestPointerLock() is asynchronous. Code must observe the pointer-lock lifecycle and handle errors or a remaining unlocked state.
Gameplay was pointer-locked before dialogue. The player clicks a choice, but dialogue closes only after an asynchronous result. The original user activation has expired, requestPointerLock() is rejected, and the pointer remains unlocked. Which exit transition is safest?
Show answer and feedback
Answer: Return to an unlocked safe mode, keep camera look disabled, show an explicit click-to-resume control, and request pointer lock from that new action while observing lifecycle events
Why: The safe transition does not assume pointer capture, does not expose camera-look behavior while unlocked, and obtains a new user action for another request. Pointer-lock failure remains separate from the confirmed narrative outcome.