add handoff-14: handoff-12 §2 backlog cleared (8 items)

This commit is contained in:
claude@clouddev1
2026-05-15 22:48:09 +00:00
parent 03dd9003df
commit 5fa3460ff6
+243
View File
@@ -0,0 +1,243 @@
# Session handoff — 2026-05-15 (14)
Fourteenth handover. This session cleared the remaining
handoff-12 backlog: every §2.1 carry-forward and the §2.2
deferred items. Eight focused commits, each a deliberate
decision the user signed off on.
**Headline: the handoff-12 §2 catalogue is now empty of
actionable items.** What remains are two flagged observations
(§3) the user should rule on, plus the standing ADR roadmap.
## State at handoff
**Branch:** `main`. Working tree clean. `origin/main` is at
`42cf851` (handoff-13); local HEAD is **8 commits ahead** — this
session's work, unpushed (the user pushes asynchronously).
Commits since handoff-13:
```
50b7825 Remove dead parse.token.* catalog entries
bcc5ad2 Matrix: pin natural candidate ordering
f1ff597 Hint: pedagogical Form-A pointer at Form B's first value slot
911a537 Walker: node-attached HintMode via Node::Hinted
9bbb96e Walker: memoize DynamicSubgrammar resolution to bound the Box::leak
90e3f5d Insert grammar: Form C type-awareness via lookahead
f46606b Runtime: schema-aware replay parsing
03dd900 Help: consume CommandNode.help_id — REGISTRY-driven in-app help
```
**Tests:** **1006 passing, 0 failing, 1 ignored** (up from 989).
The ignored test is the long-standing `` ```ignore `` doc-test
in `src/friendly/mod.rs`.
**Clippy:** clean with `nursery` lints + `-D warnings`.
## §1. What shipped — handoff-12 backlog cleared
### Dead `parse.token.*` catalog entries removed (50b7825)
The 5 structural-class + 3 lex-error entries handoff-12 §2.1
listed as unreachable are gone (catalog YAML + `keys.rs`).
### Ranker / natural candidate ordering (bcc5ad2)
The user's actual ranker need — `to` before `table` so
`add column to table T` reads in order; keywords before schema
identifiers — **already worked** via declaration-order
preservation + keywords-first sectioning in
`candidates_at_cursor`. Nothing pinned it; 8 matrix tests in
`tests/typing_surface/candidate_ordering.rs` now do. See §3 for
the `Ranker` *type* itself.
### serial/shortid pedagogical Form-A hint (f1ff597)
handoff-12 §2.2: at the first value slot of `insert into T
values (…)` for a table with auto-generated columns, the hint
now appends "(`id` auto-generated — skipped here; list columns
explicitly … to set it)". `hint_resolution_at_input` derives
the skipped columns from the post-walk `WalkContext` (Form B =
no `user_listed_columns` + table has serial/shortid columns);
the note fires only at the first slot. New
`HintResolution::form_b_autogen_skipped`, catalog key
`hint.value_slot_autogen_skipped`.
### Node-attached HintMode (911a537)
handoff-12 §2.1: the hint resolver's signature-matching (does
the expected set contain all five literal forms? an
`Ident{NewName}`?) is replaced by a grammar-declared
annotation. New `Node::Hinted { mode, inner }` wrapper; the
walker records the mode in `WalkContext::pending_hint_mode` on
entry and clears it on **any successful match** (the cursor
moved past the slot — this also undoes the leak where a failed
`Hinted` branch of a `Choice` would strand a stale mode). The
resolver reads `pending_hint_mode` directly.
Mechanism note: handoff-12 sketched threading `HintMode` through
the `Expectation` enum. ADR-0024 §HintMode only says "nodes
carry `HintMode`, the walker propagates it" — mechanism-
agnostic. The `WalkContext::pending_hint_mode` route (mirroring
the existing `pending_value_type`) was chosen as lower-risk; the
user was told and did not object.
### DynamicSubgrammar memoization (9bbb96e)
handoff-12 §2.1's `Box::leak`-per-walk. The handoff's arena
sketch was unworkable (it needs a lifetime-generic `Node` — a
major refactor). Instead `resolve_dynamic` memoizes factory
output on the schema state the factory reads (keyed by factory
fn-pointer + ctx fields). Each distinct value-list shape leaks
**once** — total leak bounded by distinct (schema × form)
combinations, not keystroke count. `TableColumn` gained `Hash`.
### Form C type-awareness (90e3f5d)
handoff-12 §2.2. Form C (`insert into T (vals)`) shared the `(`
opener with Form A, so its values weren't typed. The
explicit-`Choice`-branch split is impossible (committed-choice
semantics commit after `(` matches), so a new
`Node::Lookahead(fn(&WalkContext, &str, usize) -> Node)`
variant peeks the source: a value-literal first token routes
the paren through the typed `column_value_list` (Form B
dispatch contract); an identifier or empty paren routes to a
Form A column-name list. Form C values are now type- and
count-checked at parse time. `insert into T (` cleanly shows
Form A column candidates instead of mixed Form-A/C suggestions.
### Schema-aware replay (f46606b)
handoff-12 §2.1: `run_replay` parsed schemalessly. It now
re-snapshots the schema per line (extracted `build_schema_cache`,
shared with the interactive path) and parses with
`parse_command_with_schema` — typed-slot rejections fire at
replay parse time, matching interactive. New integration test
`replay_rejects_typed_slot_violation_at_parse_time`.
### help_id consumption (03dd900)
handoff-12 §2.1: every `CommandNode` declared an unused
`help_id`. `note_help` now iterates the command `REGISTRY` and
translates each `help_id` — a new command appears in `help`
automatically. 20 per-command catalog entries + 3 framing
entries; `help.in_app_body` removed. `CommandNode.help_id` lost
its `#[allow(dead_code)]`.
## §2. Bug found this session
**`libyml` 0.0.5 scanner panic on long space runs in
double-quoted YAML scalars.** While authoring the help entries,
a space-aligned double-quoted catalog string
(`"quit — exit"`) panicked the YAML scanner with
"String join would overflow memory bounds". Block scalars
(`|-`) are unaffected — that's why the old block-scalar help
worked. Bisected and worked around: all per-command help
entries use `|-`. **If you author new catalog entries, avoid
long internal space runs in double-quoted (`"…"`) values — use
a block scalar or keep runs short.** A catalog comment in
`en-US.yaml` records this.
## §3. CRITICAL: two flagged items needing a user decision
**3.1 The `Ranker` type is vestigial.** `completion::Ranker` /
`candidates_at_cursor_with` have no production caller passing a
non-identity ranker. The user's stated ranker need (candidate
ordering) is met by declaration-order preservation, not the
ranker layer. So `Ranker`, `identity_ranker`, and the
`candidates_at_cursor_with` variant are unused scaffolding. Per
CLAUDE.md "don't remove without confirmation" they were left
in. **Decide: remove them, or keep for a future frequency-
ranking feature?** (handoff-12 §2.1 listed the ranker as
"scaffolding-only … future work" — this is the same item, now
confirmed genuinely unused.)
**3.2 `CommandNode.hint_mode` is now genuinely dead.** The
per-command `hint_mode: Option<HintMode>` field predates the
node-attached HintMode work; HintMode is now per-*node*
(`Node::Hinted`), never per-command. The field is still
`#[allow(dead_code)]` and read by nothing. Removing it is a
safe mechanical edit across the 20 `CommandNode` declarations.
**Decide: remove it, or keep?** Not done this session (20-site
edit, separate from the HintMode mechanism change).
## §4. Open items — standing roadmap (unchanged)
handoff-12 §2's actionable backlog is cleared. What remains is
the ADR roadmap in `CLAUDE.md` "Things deliberately deferred"
(complex WHERE expressions, SQL advanced mode, indexes, m:n
convenience, snapshot/replay/undo, tutorial system, etc.) and
handoff-13's two accepted items:
- **Partial entry words classify as `DefiniteErrorAt`**
(handoff-13 §3) — the user accepted this; the matrix test
`app_commands::partial_entry_word_classifies_as_definite_error_but_completes`
documents it.
- **Matrix scope** (handoff-13 §4) — cursor coverage is
"meaningful transitions" not every byte offset; assertion (5)
is parse-layer not a live dispatch differential. User
accepted both.
## §5. Architectural delta (vs. handoff-13)
### New `Node` variants
- `Node::Hinted { mode: HintMode, inner: &'static Node }` —
node-attached hint-mode annotation.
- `Node::Lookahead(fn(&WalkContext, &str, usize) -> Node)` —
source-aware dynamic subgrammar (Form A/C discrimination).
### New `WalkContext` field
- `pending_hint_mode: Option<HintMode>` — set on `Hinted`
entry, cleared on any match.
### Walker driver
- `walk_node` split into a wrapper (clears `pending_hint_mode`
on match) + `walk_node_inner` (the dispatch).
- `resolve_dynamic` + `DYNAMIC_CACHE` — memoized
`DynamicSubgrammar` resolution.
- `Node::Lookahead` arm — not memoized (source-dependent),
returns a small node.
### New API surface
- `input_render::classify_input_with_schema` (added handoff-13,
noted here for completeness).
- `HintResolution::form_b_autogen_skipped: Vec<String>`.
- `runtime::build_schema_cache` (extracted from
`refresh_schema_cache`).
### Catalog
- Removed: `parse.token.*` (×8), `help.in_app_body`.
- Added: `hint.value_slot_autogen_skipped`,
`parse.custom.insert_form_a_missing_values` (handoff-13),
`help.intro` / `help.dsl_section` / `help.types_reference`,
`help.{app,ddl,data}.*` (×20).
## §6. How to take over
1. **Read this file, then handoff-13, then 12** for the chain.
2. **Read `CLAUDE.md`** — the working-style rules. This session
escalated every ambiguous fork (HintMode mechanism, the
`Box::leak` arena's true cost, the Form C restructure twice)
rather than deciding unilaterally.
3. **Run `cargo test`** — 1006 passing, 0 failing, 1 ignored.
4. **Run `cargo clippy --all-targets -- -D warnings`** — clean.
5. **Resolve §3** — the two flagged dead-code items — with the
user.
6. Then the standing ADR roadmap (§4) is the next structural
work; pick per the user's priorities.
### Note on the typing-surface matrix
`tests/typing_surface/` (now 144 cells) is the regression net
for everything walker/hint/completion. After any grammar or
walker change: a failing matrix cell with *correct* new
behaviour → update its snapshot
(`INSTA_UPDATE=always cargo test --test typing_surface_matrix
<family>`); a failing cell with *wrong* behaviour → the cell
earned its keep. The Form C type-awareness work this session
was guarded entirely by it.