# Handle Validity — Phase 3 (VAR_destroy) and Phase 4 (Cursor Tracking)

> **Status:** Shipped — both phases complete. Graduating to ai-workspace/docs/.
> **Phase 3 (ACU-PAT-010 stale handle):** shipped — usagePatternValidator.js, handle-validity syntax tests
> **Phase 4 (cursor tracking / hover):** shipped — handleBindingAnalysis.js, hoverProvider augmented

---

## Background

The `paramModes` registry field distinguishes four parameter-passing modes:

| Mode | Meaning |
|---|---|
| `REF` | Caller-initialised; function reads but does not write |
| `VAR` | Caller-initialised; function reads and may write |
| `VAR_init_not_required` | Function always writes; caller need not initialise first |
| `VAR_destroy` | Function reads then logically invalidates the handle |

Phase 2 implemented `VAR_init_not_required` — suppressing false ACU-VAR-002 warnings and
marking variables as initialised after the call.

`VAR_destroy` is deliberately deferred. `INDEX_CLOSE(sHandle)` does NOT unset `sHandle` in
the variable sense (it still holds its SHORT value). What it destroys is the **index cursor**
that sHandle referred to. Subsequent calls that pass sHandle as a handle argument are
logically invalid — but they are not "unset variable" errors. They need a new diagnostic
category: **stale handle**.

---

## Phase 3 — Stale Handle Detection

### What needs to happen

After `INDEX_CLOSE(sHandle)`, any subsequent call that passes `sHandle` in a `REF` or `VAR`
handle-parameter position for an index function should emit a new warning.

### Proposed diagnostic

```
ACU-PAT-010  staleIndexHandle  warning
"sHandle was passed to INDEX_CLOSE and may no longer refer to a valid index cursor."
```

(Number subject to confirmation — verify ACU-PAT-010 is not yet assigned.)

### Implementation sketch

A new per-scope state map: `closedHandles: Set<string>` (variable names).

In the main loop of `usagePatternValidator.js` (or a dedicated `handleValidator.js`):

1. When `INDEX_CLOSE(arg)` is encountered, extract the argument variable name and add it
   to `closedHandles` for the current scope.
2. For any subsequent index function call where a parameter is `REF`/`VAR` and the argument
   variable is in `closedHandles`, emit ACU-PAT-010.
3. When `INDEX_OPEN_STANDARD/CUSTOM(...)` is encountered and the output parameter
   (`VAR_init_not_required`) resolves to a variable name already in `closedHandles`, remove
   it — the handle has been re-opened.

### Argument extraction

The existing `extractArgAt()` helper in `variableValidator.js` extracts the raw text of the
Nth argument. It should be promoted to `parser.js` or a shared utility so both the variable
validator and the handle validator can use it without coupling.

### Scope of `VAR_destroy` in the registry

Only index functions currently carry `VAR_destroy`:

| Function | Param | Notes |
|---|---|---|
| `INDEX_CLOSE` | arg 0 | The primary invalidation point |

Array handles are explicitly **not** `VAR_destroy` — `FREE_SORTED_ARRAY` releases the
in-memory array but the SHORT variable retains its integer value and can be re-used
immediately with `INIT_SORTED_ARRAY`. See the array lifecycle section of the language spec.

### TDD requirement

Write a failing test in `test/syntax-tests/handle-validity/` before implementing.
The test should cover:
- Clean re-open after close (no warning)
- Use of closed handle in load/read (warning)
- Multiple handles in same scope — only the closed one warns

---

## Phase 4 — Cursor Tracking

### What needs to happen

The `bindsHandle` and `cursorTableFromHandle` fields already exist in the functions registry
for several index functions:

```js
INDEX_OPEN_STANDARD: {
    bindsHandle: { handleParam: 2, tableCodeParam: 0 },
    ...
}
INDEX_READ_BY_KEY: {
    cursorTableFromHandle: 0,
    ...
}
```

Phase 4 uses this metadata to build a runtime model: after `INDEX_OPEN_STANDARD("MD", 0, sHandle)`,
the validator knows that `sHandle` is bound to table `"MD"`. Subsequent `INDEX_READ_BY_KEY(sHandle, "=")`
calls can then be annotated with which table's cursor is being advanced.

### Proposed use cases

1. **Hover information** — hovering over `INDEX_READ_BY_KEY(sHandle, "=")` shows "Advances cursor
   on MD (Member Details)."
2. **Completion hints** — after opening a handle on "MD", context-aware field name suggestions.
3. **Diagnostic refinement** — ACU-PAT checks can report which specific table's cursor was
   left open or misused.

### Implementation sketch

A new per-scope (or per-document) map: `handleBindings: Map<varName, tableCode>`.

- On `INDEX_OPEN_STANDARD(tableCode, indexNo, handle)`: parse tableCode arg (if string
  literal), extract handle arg variable name, store `handleBindings.set(varName, tableCode)`.
- On `INDEX_CLOSE(handle)`: remove the binding (or mark invalid).
- On `INDEX_READ_BY_KEY(handle, direction)`: look up `handleBindings.get(handle)` to resolve
  the table being iterated.

This information flows into the hover and completion providers, not purely into diagnostics.
It is **read infrastructure**, not yet a standalone diagnostic pass.

### Dependencies

Phase 4 requires Phase 3 to be complete (knowing whether a handle is valid is a prerequisite
for reasoning about its table binding).

---

## Graduation

This spec graduates to `ai-workspace/docs/` once both phases are shipped. If Phase 4 is
indefinitely deferred, promote with Phase 3's work noted and Phase 4 moved to a separate
future-work doc.
