ADR-0024 round-5 follow-up: surface tail-Optional expectations

Pre-Phase-D, `save ` parsed as a complete `save` command, so
the completion engine had nothing to mine: `as` never surfaced
as a Tab candidate. This is the round-5 gap the handoffs have
been tracking.

`WalkResult` gains a `tail_expected: Vec<Expectation>` field.
The walker's top-level `Matched` branch copies the outer
shape's skipped-Optional expectations into it. `expected_at_input`
returns `tail_expected` on `Match` so the completion engine
sees the optional-suffix continuations and offers them as Tab
candidates.

`hint_mode_at_input` deliberately does NOT consume
`tail_expected` — surfacing prose like "Type a name" at the
end of a valid command would be misleading. A new private
`expected_for_hint` returns empty on `Match` to preserve this.
The split distinguishes "valid + could continue" (completion
helps) from "invalid + must continue" (hint resolver helps).

Tests:
- `save ` Tab → `as` (new test, the original round-5 gap).
- `messages ` Tab → `short` and `verbose` (same shape).
- Existing `hint_mode_none_for_complete_command` stays green
  because hint resolver ignores tail_expected.
- 830 total passing, 0 failing, 1 ignored. Clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-15 17:50:31 +00:00
parent abebd7944f
commit 8188fa5ee1
3 changed files with 103 additions and 16 deletions
+12
View File
@@ -164,4 +164,16 @@ pub struct WalkResult {
pub outcome: WalkOutcome,
pub matched_path: MatchedPath,
pub per_byte_class: Vec<ByteClass>,
/// Optional-Optional expectations the walker could have
/// accepted but didn't because the outer shape ran out at a
/// node boundary (ADR-0024 §architecture, round-5 follow-up).
///
/// Populated on `WalkOutcome::Match` so completion can offer
/// optional-suffix candidates at the end of a valid command
/// — e.g., after typing `save` the walker matches the
/// Optional `as` as skipped, the suffix carries it here, and
/// the completion engine surfaces `as` as a Tab candidate.
/// Empty on the non-Match outcomes — those carry expected
/// information inside the outcome variant itself.
pub tail_expected: Vec<Expectation>,
}