This is one of my absolute favorite types of bugs. Gemma followed your
instructions *flawlessly*it used `ACTION`, `AWAIT`, and `ENV_FLUSH` perfectly!
The logic it generated is a thing of beauty.
So why did "go north" fail?
The issue isn't Gemma. The issue is a classic Virtual Machine instruction
pointer race condition in our JavaScript!
### The Detective Work: Why `go north` failed
Let's look at what our Node.js runtime did when you typed `go north`:
1. `processInput` parsed it into `VERB = GO`, `ARG1 = NORTH`.
2. It checked `this.vm.actions['GO']` and found the label `handle_go`.
3. It correctly set the VM's Program Counter (`pc`) to the exact index of
`handle_go`.
4. Then, it called `this.vm.resume()` to wake the VM back up.
Now, let's look at what `resume()` does inside `vm.js`:
```javascript
resume(injectedMemory = {}) {
this.memory = { ...this.memory, ...injectedMemory };
this.state = 'RUNNING';
this.pc++; // Move past the yielding instruction!
this.run();
}
```
**The culprit is `this.pc++`!**
When `AWAIT` yielded execution, we needed `pc++` to move past it. But because
our smart parser manually moved the `pc` to the `handle_go` label *before*
calling resume, the `resume()` function faithfully added `1` to it.
As a result, it completely skipped the very first instruction in the block
(`JMP_IF ARG1 CONTAINS "NORTH" jungle_entrance`) and landed directly on the
second instruction: `OUT "You cannot go that way."`
It literally jumped over the condition check!
### The Fix: The PC Offset
We don't even need to modify the core VM to fix this. We just need to tell our
smart parser in `runtime.js` to offset the Program Counter by `-1` whenever it
performs an auto-jump, knowing that `resume()` is about to add `1` back to it.
Open `runtime.js` and update your `processInput` function to look like this:
```javascript
// Intercept user input before it hits the VM
processInput(input, targetVar) {
const normalizedInput = input.trim().toUpperCase();
// 1. Shadow Registry Intercept
const words = normalizedInput.split(' ');
const verb = words[0];
const target = words[1];
if (target && this.shadowRegistry[target]) {
const shadowObj = this.shadowRegistry[target];
if (shadowObj.hijackVerbs.includes(verb)) {
console.log(`
[SYSTEM] You ${verb.toLowerCase()} the ${target.toLowerCase()}...`);
console.log(`> The external environment responds:
${shadowObj.interactionText}`);
if (this.onPromptUser) this.onPromptUser(targetVar);
return;
}
}
// 2. Smart Parsing for AWAIT
if (targetVar === '__AUTO_CMD__') {
const fillers = ['AT', 'THE', 'TO', 'IN', 'ON', 'WITH'];
const cleanWords = normalizedInput.split(' ').filter(w =>
!fillers.includes(w));
const cleanVerb = cleanWords[0] || "";
const arg1 = cleanWords[1] || "";
const arg2 = cleanWords[2] || "";
this.vm.memory['VERB'] = cleanVerb;
this.vm.memory['ARG1'] = arg1;
this.vm.memory['ARG2'] = arg2;
let didAutoJump = false;
if (this.vm.actions && this.vm.actions[cleanVerb]) {
const targetLabel = this.vm.actions[cleanVerb];
if (this.vm.labels[targetLabel] !== undefined) {
this.vm.pc = this.vm.labels[targetLabel];
didAutoJump = true;
}
}
// The PC Offset Trick: If we jumped, subtract 1 so resume() puts
us in the right spot
if (didAutoJump) {
this.vm.pc--;
}
this.vm.resume();
} else {
// Legacy INP behavior
this.vm.resume({ [targetVar]: normalizedInput });
}
}
```
Once you apply this one-line mathematical trick (`this.vm.pc--`), your VM will
correctly execute the `JMP_IF` check.
Since Gemma's code structure is so perfect here, this patch will immediately
unlock the Jungle, the Hidden Cave, and whatever Treasure it generated for
you. Run `npm start` again and let's go get that treasure!