# Missing Assertion Literal Plan
## Decision
- Canonical syntax: `^ & body.Item == missing`
- Also support inequality: `^ & body.Item != missing`
- Reuse the existing internal missing-path state in the assertion engine instead of adding a new runtime concept.
- Do not overload `===`.
- Do not add `undefined` in the first slice unless a compatibility need shows up later.
## Goal
Add a first-class `missing` literal for assertion comparisons so authors can explicitly assert that a response path is absent, while preserving the existing distinction between `missing` and `null`.
## Scope
This slice should cover:
- parser/runtime support for `missing` in assertions and guards
- focused runtime and verify coverage
- root docs and website docs
- at least one dedicated example
- VS Code hover/completion docs for the new literal
This slice should not cover:
- any `=== MISSING` special case
- an `undefined` alias
- changes to JSON literal parsing or schema declarations
## Implementation Plan
### 1. Assertion parser and runtime
Primary file: `src/request/assertion.rs`
Planned changes:
- Add a dedicated parse branch for bare `missing` before the current bare-word variable fallback.
- Map that branch onto the existing internal missing-path representation already used in mismatch reporting and `null` differentiation.
- Keep `==` and `!=` semantics unchanged; this should be a new literal, not a new operator.
- Ensure guarded assertions like `[ & body.user.avatar == missing ]` behave consistently with ordinary comparisons.
- Leave bare `undefined` on the existing variable-resolution path for now.
Why this seam:
- The runtime already distinguishes `Missing` from `null`; the missing piece is syntax, not evaluation machinery.
- Reusing the internal value avoids new comparison rules, new report shapes, and new error text.
### 2. Test coverage
Primary file: `src/request/assertion.rs`
Add focused unit coverage for:
- `^ & body.value == missing` passes when the field is absent
- `^ & body.value != missing` passes when the field is present
- `missing` stays distinct from `null`
- guard expressions can use `missing`
- bare `undefined` still resolves as a variable and is not treated as a literal
Add one verify-facing regression if needed in `tests/verify_cli.rs` to lock in that `hen verify` accepts the new literal and reports other assertion errors normally.
### 3. Root docs
Files:
- `syntax-reference.md`
- `README.md`
Planned updates:
- Expand the typed-literal wording from `true`, `false`, and `null` to include `missing`.
- Document that `missing` means the selected response path does not exist, and that it is different from `null`.
- Add one short example showing both cases:
- `^ & body.deletedAt == null`
- `^ & body.archivedAt == missing`
- Clarify that `===` remains schema/scalar validation only.
### 4. Website docs
Primary file:
- `website/docs/reference/captures-and-assertions.md`
Audit targets:
- `website/docs/reference/json-selection.md`
- any recipe page that discusses missing-vs-null behavior
Planned updates:
- Mirror the root assertion docs and add `missing` to the typed-literal explanation.
- Add a short note that missing paths are preserved distinctly during evaluation.
- Include one concrete absence assertion example.
- Update any wording that currently implies `null` is the only built-in absence-like comparison surface.
### 5. Examples
Preferred approach:
- Add a dedicated example file, likely `examples/missing_assertions.hen`.
Why a new example:
- The current open example `examples/map_request.hen` is about mapped requests, not path absence.
- A dedicated file keeps the missing-vs-null distinction easy to discover and avoids bending another example away from its main teaching goal.
Example should demonstrate:
- a field that is explicitly `null`
- a field that is absent
- `== missing`
- `!= missing`
- one short comment explaining the distinction
### 6. VS Code extension
Primary files:
- `editors/vscode-hen/reference.js`
- `editors/vscode-hen/server/server.js`
- `editors/vscode-hen/server/completion-data.js`
Planned updates:
- Add a reference entry for `missing` so hover text explains the literal and its difference from `null`.
- Add `missing` to the hover token recognition path in `server/server.js`.
- Update the `==` / `!=` operator docs to mention `missing` alongside `null` where relevant.
- Consider adding a second assertion snippet example that expands to `^ & body.field == missing`.
Non-goal for the first slice:
- special semantic-token styling for `missing`, unless it is trivial once hover support is in place.
### 7. Validation
Runtime and parser:
- run focused assertion unit tests in `src/request/assertion.rs`
- run the adjacent verify regression if one is added
Editor:
- `cd editors/vscode-hen && npm run build`
Website:
- `cd website && npm run typecheck`
- `cd website && npm run build`
## Main Unknowns And Questions
### 1. Compatibility risk: bare `missing` becomes reserved
Today, unknown bare operands fall through to variable lookup. After this change, bare `missing` in assertions will stop meaning "variable named missing" and start meaning the literal missing state.
Mitigation:
- document the change clearly
- keep `$missing` working for explicit variable references
- add one regression test so the new reservation is intentional and stable
### 2. Whether to support `missing` in guards immediately
Recommendation:
- yes, if the parser path is shared, because it keeps assertion expressions uniform
### 3. Whether to add `undefined` as an alias later
Recommendation:
- no for the first slice
- ship one canonical word first so docs, examples, and hover text stay crisp
## Success Criteria
- `^ & body.field == missing` works
- `^ & body.field != missing` works
- `missing` remains distinct from `null`
- `===` remains untouched
- docs, website docs, and one example all show the new syntax
- VS Code hover recognizes `missing`
- focused Rust tests, website checks, and extension build all pass