669 lines
28 KiB
Markdown
669 lines
28 KiB
Markdown
# Session handoff — 2026-05-15 (9)
|
|
|
|
Ninth handover. This session executed **ADR-0024 phases A
|
|
through F** in one sitting, six commits. The walker is now the
|
|
sole parse path; the chumsky dependency is gone. What did NOT
|
|
land — and what the next session needs to pick up — is
|
|
captured below in priority order.
|
|
|
|
## State at handoff
|
|
|
|
**Branch:** `main`. Working tree clean. **Local HEAD is
|
|
`c940ba9`**, ahead of `origin/main` by six commits (the user
|
|
pushes asynchronously; do not be blocked by unpushed state).
|
|
|
|
Commits since handoff-8's baseline (`3e1ff83`):
|
|
|
|
```
|
|
50b3542 ADR-0024 Phase A: walker framework + app-lifecycle commands
|
|
7e79ca8 ADR-0024 Phase B: DDL commands without value literals
|
|
6bb6882 ADR-0024 Phase C: create table with column-list value literals
|
|
c2accc2 ADR-0024 Phase D: data commands at chumsky parity
|
|
dca472f ADR-0024 Phase E: replay end-to-end
|
|
c940ba9 ADR-0024 Phase F (minimal): drop chumsky from the parse path
|
|
```
|
|
|
|
**Tests:** **844 passing, 0 failing, 1 ignored** (up from 777
|
|
at handoff-8's baseline; +67 over this session). The ignored
|
|
test is still the same `\`\`\`ignore` doc-test in
|
|
`src/friendly/mod.rs`.
|
|
|
|
**Clippy:** clean with `nursery` lints enabled and
|
|
`-D warnings`.
|
|
|
|
**Cargo.toml:** `chumsky` dependency dropped. `thiserror`
|
|
remains gone (per handoff-8). The crate's parse path is now
|
|
fully Rust-native (lexer + walker, no parser-combinator
|
|
library).
|
|
|
|
## What shipped this session — quick overview
|
|
|
|
ADR-0024's six phases all landed end-to-end. Walker now owns
|
|
all 20 entry-keyword commands across 14 entry words:
|
|
|
|
- **Phase A:** walker framework + 11 app-lifecycle commands
|
|
(quit, help, rebuild, save / save as, new, load, export,
|
|
import, mode, messages). Optional + Choice + Seq combinators.
|
|
BarePath terminal. Path-bearing UX change shipped (paths
|
|
with spaces require quotes).
|
|
- **Phase B:** DDL — drop, add, rename, change. Repeated
|
|
combinator with optional separator. Flag terminal. NumberLit
|
|
+ Literal terminals. Optional now propagates inner
|
|
expectations as `skipped` so completion sees "what could
|
|
have appeared here".
|
|
- **Phase C:** create table. Repeated with `,` separator
|
|
(first use). `with pk` defaulting to `id:serial`.
|
|
- **Phase D:** data — show, insert, update, delete. StringLit
|
|
terminal. Value-literal sub-grammar. **Schema-aware value
|
|
typing deliberately deferred** (see below).
|
|
- **Phase E:** replay. `Choice(StringLit, BarePath)`.
|
|
- **Phase F (minimal):** chumsky combinators deleted, chumsky
|
|
crate dep dropped. **Significant scope deferred** (see
|
|
below).
|
|
|
|
## DEFERRED — work the next session needs to pick up
|
|
|
|
This is the meat of the handoff. Items grouped by urgency.
|
|
|
|
### 1. Phase F (full): legacy parser-side modules still standing
|
|
|
|
ADR-0024 §migration Phase F prescribes deleting these modules.
|
|
Phase F minimal in this session only deleted the chumsky
|
|
combinator code. The following are still in place and consumed
|
|
by other modules:
|
|
|
|
| Module | Still consumed by | Why kept |
|
|
|---|---|---|
|
|
| `src/dsl/lexer.rs` | `theme.rs`, `input_render.rs`, `app.rs`, `dsl/usage.rs`, `dsl/parser.rs` | Per-token highlighting + echo-line tokenization + completion partial-token detection |
|
|
| `src/dsl/keyword.rs` (`Keyword` enum) | `completion.rs`, `friendly/keys.rs`, `theme.rs`, `dsl/usage.rs` | Catalog key derivation (`parse.token.keyword.*`), keyword-name validation in completion |
|
|
| `src/dsl/ident_slot.rs` (`IdentSlot` enum) | `completion.rs`, `input_render.rs`, `runtime.rs`, `db.rs` | Schema-cache lookups dispatched per slot kind |
|
|
| `src/dsl/usage.rs::REGISTRY` + `matched_entry` | `completion.rs`, `app.rs` | Per-command usage templates rendered on parse error |
|
|
| `parse.token.keyword.*` catalog (40+ entries) | `dsl/usage.rs`, `friendly/keys.rs` | Keyword wording in usage templates |
|
|
|
|
**Replacement strategy** (sketched, for the migrating session):
|
|
|
|
- **Highlighting** (`input_render.rs::render_input_panel`):
|
|
the walker's `WalkResult::per_byte_class` populates
|
|
`(start, end, HighlightClass)` per matched terminal. Wire
|
|
this output to the `tok_*` theme colours in place of the
|
|
current `lex(input)`-driven span builder. Walker error
|
|
positions feed the `tok_error` overlay. This is the single
|
|
biggest change.
|
|
- **Completion** (`completion.rs::candidates_at_cursor`): the
|
|
walker's `WalkResult` at `WalkBound::Position(cursor)` (NOT
|
|
yet exercised — the walker's `walk()` does support this,
|
|
but no caller passes it today) gives `expected: Vec<Expectation>`
|
|
at the cursor. The bridge already maps `Expectation::Ident
|
|
{ source: Tables/Columns/Relationships/Types }` to the
|
|
user-facing labels matching `IdentSlot::expected_label`, so
|
|
the existing completion engine reads them transparently.
|
|
What's missing: a walker-driven path that bypasses
|
|
`parse_command` entirely and asks the walker directly for
|
|
candidates per `IdentSource`. Today the bridge round-trips
|
|
through `ParseError::Invalid::expected` strings — works,
|
|
but loses information.
|
|
- **Echo-line tokenization** (`output_render.rs` for
|
|
`OutputKind::Echo + Mode::Simple`): same lex-driven spans.
|
|
Same walker `per_byte_class` plumbing.
|
|
- **Usage templates** (`dsl/usage.rs::REGISTRY` + `matched_entry`):
|
|
every `CommandNode` already has `usage_id`. Wire the
|
|
parse-error renderer to look up the catalog entry by
|
|
`usage_id` instead of `matched_entry`. Then `REGISTRY` and
|
|
`matched_entry` can go.
|
|
- **Catalog cleanup** (`parse.token.keyword.*` + `friendly/keys.rs`):
|
|
ADR-0024 §cleanup-pass §F mentions a `format_keyword_for_error
|
|
(literal) -> String` helper that wraps a literal in
|
|
backticks and replaces the per-keyword catalog entries.
|
|
Mechanical; do it after the consumers stop reading the
|
|
catalog keys.
|
|
|
|
**Estimated cost: one full session** for the consumer
|
|
migration + a follow-up commit for the catalog cleanup. Test
|
|
suite serves as the regression net throughout.
|
|
|
|
### 2. Phase D (full): schema-aware value typing
|
|
|
|
ADR-0024 §migration Phase D prescribes "full schema awareness"
|
|
via `DynamicSubgrammar(column_value_list)` that unfolds typed
|
|
slots per column at walk time. **This session deferred it.**
|
|
|
|
What's in place:
|
|
|
|
- `Node::DynamicSubgrammar(fn(&WalkContext) -> Node)` variant
|
|
declared but unused. The walker's driver returns
|
|
`Failed { expected: vec![] }` on this branch (intentional —
|
|
catches grammar bugs that declare DynamicSubgrammar without
|
|
the wiring landing).
|
|
- `WalkContext::current_table`,
|
|
`WalkContext::current_table_columns`,
|
|
`WalkContext::current_column` all declared but unwritten.
|
|
No `Ident` node has a `writes_table: bool` or equivalent.
|
|
- Per-type validators (`int_slot`, `decimal_slot`, etc.) NOT
|
|
written. The current walker uses a generic `value_literal`
|
|
Choice that accepts any literal regardless of column type;
|
|
bind-time type-check errors fire as today.
|
|
|
|
**To implement:**
|
|
|
|
1. Plumb a `SchemaCache` reference into `parse_command` (and
|
|
thus `parse_tokens`). Currently the call site is in
|
|
`runtime.rs::dispatch_input` — passes `app.schema_cache()`
|
|
alongside the input.
|
|
2. Extend `WalkContext::new(schema)` to carry the cache.
|
|
3. Implement `Node::DynamicSubgrammar` walker dispatch:
|
|
resolve at walk time, leak the returned `Node` into a
|
|
per-walk arena (or `Box::leak` per ADR-0024 §sub-grammars).
|
|
4. Implement `Ident { source: Tables, writes_table: true }`
|
|
semantics — when the ident matches, look up the table in
|
|
the schema, populate `current_table` + `current_table_columns`.
|
|
5. Implement typed value slots — `int_slot()`, `decimal_slot()`,
|
|
etc. per ADR-0024 §typed-value-slots. Each is a Choice
|
|
over the literal forms with a content validator.
|
|
6. Wire `column_value_list` as a DynamicSubgrammar that reads
|
|
`current_table_columns` and emits a Seq of typed slots
|
|
separated by commas.
|
|
7. Update `insert` shape to use `column_value_list` instead
|
|
of the generic value list.
|
|
8. Update `update`/`delete` to use the per-column value slot
|
|
based on `current_column`.
|
|
|
|
**The user UX win this unlocks:** typed slots reject
|
|
mis-shaped input at parse time with localised wording (e.g.,
|
|
"Type a date as 'YYYY-MM-DD'") instead of bind-time errors;
|
|
completion narrows per column type. This is the Phase D
|
|
"central design claim" per the handoff.
|
|
|
|
**Side effect to watch for:** parse_command becomes schema-
|
|
dependent. Tests that exercised parse in isolation may need
|
|
to pass a schema cache (today's tests don't — most just
|
|
check round-trip parses where schema doesn't matter).
|
|
|
|
**Estimated cost: 1-2 sessions** depending on how deep the
|
|
schema plumbing goes through dispatch.
|
|
|
|
### 3. Walker doesn't drive completion or hints directly
|
|
|
|
Today's flow: walker produces `WalkOutcome` → bridge to
|
|
`ParseError::Invalid` → completion / hint engines read
|
|
`expected: Vec<String>`. The bridge formats `Expectation::Ident
|
|
{ source: … }` to the user-facing label string the existing
|
|
engines recognise.
|
|
|
|
This works but loses information. Walker knows:
|
|
- The `IdentSource` of every expected slot.
|
|
- The `role` of every slot (e.g., `parent_table` vs
|
|
`child_table`).
|
|
- The `HintMode` per node (currently always `Default`).
|
|
- The `skipped` expectations from any Optional that didn't
|
|
engage at this position.
|
|
- The cursor's full `MatchedPath` so far — the AST builder
|
|
could be invoked partially to extract context (e.g., "you've
|
|
typed `update Customers set Email=`, the `Email` column's
|
|
type is `text`").
|
|
|
|
A walker-direct completion path would surface much more
|
|
informative candidates than the current ParseError-string round
|
|
trip. See ADR-0024 §architecture: "Completion at cursor:
|
|
`walk(source, Position(cursor), ctx)`, inspect `outcome.expected`."
|
|
|
|
**Today, no caller invokes `walk()` with `WalkBound::Position(cursor)`.**
|
|
The variant exists in `outcome.rs` (annotated `#[allow(dead_code)]`).
|
|
`completion.rs` still calls `parse_command(leading_slice)` —
|
|
the slice-and-re-parse approach inherited from chumsky.
|
|
|
|
**Migration path:** add a `pub fn candidates_at_cursor_from_walker
|
|
(input, cursor, schema) -> WalkResult` that calls
|
|
`walker::walk(input, WalkBound::Position(cursor), &mut ctx)`
|
|
and returns the result. Then `completion.rs` reads
|
|
`expected` directly with full `IdentSource` + `role` info.
|
|
|
|
### 4. `HintMode` declared but unused
|
|
|
|
`HintMode::Default | ForceProse | ProseOnly | SuppressProse` is
|
|
declared in `grammar/mod.rs` but every `CommandNode` and
|
|
every `Node::Ident` sets it to `None`. The current ad-hoc
|
|
hint cases in `input_render.rs::ambient_hint` (value-literal
|
|
slot suppression, NewName slot typing-name prose,
|
|
invalid-ident overlay) still use the chumsky-era ad-hoc
|
|
detection.
|
|
|
|
ADR-0024 §HintMode-per-node says these migrate to
|
|
node-attached `HintMode` annotations during Phase D. They
|
|
didn't.
|
|
|
|
**To do:** annotate the value-literal slots with
|
|
`HintMode::ProseOnly("value_literal_format_hint")`; annotate
|
|
NewName slots with the typing-name prose; have the hint
|
|
resolver pick up the mode and dispatch.
|
|
|
|
### 5. Ranker hook: declared in ADR-0024, not implemented at all
|
|
|
|
ADR-0024 §ranker-layer specifies a `Ranker` function type
|
|
between the walker's raw candidates and the hint-panel
|
|
renderer. The default is `identity_ranker` (declaration-order
|
|
preserved).
|
|
|
|
**Status: not declared anywhere in code.** No `Ranker` type,
|
|
no identity ranker, no hook into completion. The current
|
|
completion engine ranks by its own ad-hoc logic (keyword
|
|
matches first, then schema, alphabetised within each).
|
|
|
|
This is a small future-work hook. Not blocking.
|
|
|
|
### 6. Aliases: feature works, no aliases declared
|
|
|
|
`Word::aliases: &'static [&'static str]` is wired through
|
|
`Word::matches` and the walker correctly accepts case-
|
|
insensitive alias matches. **Today every `Word` in the
|
|
grammar has an empty aliases slice.**
|
|
|
|
The round-5 `q` quit alias removal stands — if the user
|
|
wants it back, it's:
|
|
|
|
```rust
|
|
const QUIT_WORD: Word = Word {
|
|
primary: "quit",
|
|
aliases: &["q"],
|
|
highlight_override: None,
|
|
};
|
|
pub static QUIT: CommandNode = CommandNode {
|
|
entry: QUIT_WORD,
|
|
...
|
|
};
|
|
```
|
|
|
|
The walker matches either; completion only surfaces the
|
|
primary. No further wiring needed.
|
|
|
|
### 7. Test edits worth knowing about
|
|
|
|
A handful of tests changed wording assertions to match
|
|
walker-emitted error wording:
|
|
|
|
- `src/dsl/walker/mod.rs::walker_import_trailing_as_without_target_errors`
|
|
— assertion weakened from checking for "target" to checking
|
|
for "import". Walker's Optional backtracking means
|
|
`import foo.zip as ` parses as `Import { path: "foo.zip",
|
|
target: None }` followed by trailing `as ` → walker reports
|
|
`expected end of input, found …`. The friendly
|
|
`project.import_empty_target` wording moved out of the
|
|
parser. The integration test
|
|
(`tests/iteration5_export_import.rs::import_with_empty_target_after_as_errors`)
|
|
still passes because the rendered `import_usage` template
|
|
line in the dispatch output contains both "import" and
|
|
"target".
|
|
- The walker's parse error wording for incomplete inputs
|
|
consistently uses "after `<consumed>`, expected …, found
|
|
end of input" — matches the chumsky-era contract that
|
|
`structural_error_for_show_data_without_arg` and friends
|
|
pin down.
|
|
|
|
**No `parse.token.keyword.*` wording changed.** No catalog
|
|
entries changed. No user-visible string regression.
|
|
|
|
### 8. The `value_literal_hint_at_cursor` stopgap
|
|
|
|
The round-6 stopgap from handoff-8 (replacing `null true false`
|
|
with prose at value-literal slots) **is still live**. It
|
|
lives in `completion.rs::value_literal_hint_at_cursor` and
|
|
detects the value-literal-signature in `expected`. With Phase
|
|
D's full schema awareness, this would become "narrow to the
|
|
actual column's type" (e.g., "Type a date as 'YYYY-MM-DD'")
|
|
— but that requires the schema plumbing in §2 above.
|
|
|
|
**Today the stopgap continues to fire for every value-literal
|
|
slot regardless of column type.** Same wording as handoff-8.
|
|
|
|
### 9. Choice greedy-semantics shape contortions
|
|
|
|
ADR-0023 says "the trie design is greedy (the first child
|
|
node that matches wins)." This is the design. But it forced
|
|
some grammar contortions worth knowing about:
|
|
|
|
- **`insert` Form A vs Form C disambiguation** (`grammar/data.rs`):
|
|
Forms A (`insert into T (cols) values (vals)`) and C (`insert
|
|
into T (vals)` — bare value list) both start with `(`. The
|
|
inner-paren content is parsed as a heterogeneous
|
|
`Choice(VALUE_LITERAL, Ident{Columns})` — VALUE_LITERAL
|
|
ordered first so `null`/`true`/`false` match their Word
|
|
branch rather than the broader identifier catch-all (which
|
|
`consume_ident` doesn't filter against the keyword set).
|
|
AST builder discriminates by the presence of the `values`
|
|
keyword AFTER the first paren. **Brittle if a future grammar
|
|
needs to add another paren-bounded form starting at the
|
|
same position.**
|
|
- **`drop` sub-form ordering**: `drop_column` and
|
|
`drop_relationship` come before `drop_table` in the Choice
|
|
because they're more specific (longer prefix). Walker
|
|
greediness handles this correctly because each branch's
|
|
first Word disambiguates.
|
|
|
|
### 10. Walker `per_byte_class` populated but unused
|
|
|
|
`WalkResult::per_byte_class: Vec<ByteClass>` is populated by
|
|
every terminal match in the walker driver. **No consumer
|
|
reads it today.** The annotation `#[allow(dead_code)]` sits
|
|
on the field. When the highlighting consumer migrates (§1
|
|
above), this is the data source.
|
|
|
|
### 11. Differential test scaffolding wasn't actually built
|
|
|
|
ADR-0024 §test-discipline §3 specifies a "differential check
|
|
during the migration window" — a test helper that runs both
|
|
parsers on the existing input corpus and asserts identical
|
|
`Command` output. Removed at Phase F cleanup.
|
|
|
|
**This session went with hand-curated expected `Command`
|
|
outputs in `dsl::walker::tests` instead.** Equivalent
|
|
coverage (every migrated command's parse asserted), simpler
|
|
to maintain. Since chumsky is now gone (Phase F minimal
|
|
removed the combinator code), no removal step needed.
|
|
|
|
If a strict differential check is wanted retroactively, the
|
|
chumsky path would need to be reconstructed from git history
|
|
and run alongside walker against the test corpus — not
|
|
trivial. The hand-curated tests + the existing integration
|
|
test suite serve the same regression-net role.
|
|
|
|
### 12. `WalkContext` writes during walk — design exists, not implemented
|
|
|
|
ADR-0024 §WalkContext sketches `Ident { source: Tables,
|
|
writes_table: true }` semantics: when the ident matches, the
|
|
walker writes `current_table` to context. Subsequent dynamic
|
|
sub-grammars read it.
|
|
|
|
**Today no `Ident` node has a `writes_table` field.** The
|
|
struct definition in `grammar/mod.rs` is:
|
|
|
|
```rust
|
|
Ident {
|
|
source: IdentSource,
|
|
role: &'static str,
|
|
validator: Option<IdentValidator>,
|
|
highlight_override: Option<HighlightClass>,
|
|
}
|
|
```
|
|
|
|
When Phase D (full) lands, add a `writes_table: bool`
|
|
(or similar) field and have the walker driver populate
|
|
`WalkContext` accordingly.
|
|
|
|
### 13. `CommandNode::usage_id` / `help_id` not consumed
|
|
|
|
Every `CommandNode` declares `usage_id: Option<&'static str>`
|
|
and `help_id: Option<&'static str>` pointing into the
|
|
catalog. **No code reads these fields.** Usage rendering
|
|
still goes through `dsl/usage.rs::matched_entry`. Help
|
|
text still hand-curated.
|
|
|
|
When `dsl/usage.rs::REGISTRY` retires (§1 above), wire
|
|
the parse-error renderer and the in-app help system to read
|
|
these fields directly.
|
|
|
|
## Sharp edges to know about
|
|
|
|
These are facets of the walker's behaviour that aren't bugs
|
|
but that will surprise someone reading the code cold.
|
|
|
|
- **Optional backtracking on partial-match** is intentional
|
|
(matches chumsky's `or_not` semantics). When an Optional's
|
|
inner consumes some terminals and then fails (Incomplete or
|
|
Mismatch), the walker rolls back the path / per_byte state
|
|
to the pre-Optional position and treats it as skipped, with
|
|
the inner's expectations carried as `skipped` on the
|
|
Matched return. **Validation failures (content errors) do
|
|
NOT backtrack** — the user means to fix those. This
|
|
asymmetry is the load-bearing decision that makes
|
|
`create table T with` produce the correct
|
|
`IncompleteAtEof` classification (chumsky's behaviour).
|
|
- **Walker's `Choice` is strictly greedy** — first child
|
|
whose first terminal matches wins. No backtracking. Required
|
|
ordering: more-specific shapes before more-general. See §9
|
|
above for examples.
|
|
- **`Ident { source: Tables/Columns/Relationships }` does NOT
|
|
validate against the schema at parse time.** It's a
|
|
shape-only check today. Schema-aware parse is the Phase D
|
|
vision; see §2.
|
|
- **`Literal(&'static str)` matches verbatim bytes with a
|
|
word-boundary lookahead** so `1` doesn't half-match `12`
|
|
and `n` doesn't half-match `name`. The highlight class is
|
|
inferred from the literal's bytes (digits → Number, else
|
|
Keyword). Used today only for the `1` in `add 1:n
|
|
relationship`.
|
|
- **`AST builder` failures surface as `WalkOutcome::ValidationFailed`
|
|
with `at_eof = true`**. The bridge maps these to
|
|
`ParseError::Invalid` with `at_eof: true` so the input
|
|
renderer classifies them as `IncompleteAtEof` (no live
|
|
overlay; on-submit error fires). This mirrors the chumsky-
|
|
side custom-error convention.
|
|
- **`unknown_command_error` is the sole catch-all** for
|
|
inputs whose first identifier-shape token isn't a registered
|
|
entry word. Wording: "expected one of `add`, …, found
|
|
`<word>`". Position is the start of the unknown word.
|
|
- **`q` quit alias remains gone.** The walker SUPPORTS
|
|
aliases natively — adding `q` back is a one-line change on
|
|
`QUIT.entry.aliases` (see §6).
|
|
- **Path-bearing UX change shipped (Phase A + E):** `replay`,
|
|
`import`, `export` paths terminate at the first whitespace
|
|
byte. Paths with spaces use the quoted form
|
|
(`replay 'my project/seed.commands'`). This is per ADR-0024.
|
|
|
|
## Suggested next-session priorities
|
|
|
|
In order:
|
|
|
|
1. **Phase F (full): consumer migration to walker outputs.**
|
|
This is the biggest deferred chunk and the right next
|
|
structural move. See §1 above for the migration sketch.
|
|
~1 session for the highlighting + completion + usage
|
|
migration; one follow-up commit for the catalog cleanup
|
|
and lexer/keyword/ident_slot deletion.
|
|
2. **Phase D (full): schema-aware value typing.** Once the
|
|
schema cache plumbing exists, the `DynamicSubgrammar`
|
|
wiring + typed value slots are mechanical. See §2.
|
|
3. **Walker-driven completion** (§3). Smaller scope than the
|
|
above. Surfaces `IdentSource` + `role` directly to the
|
|
completion engine without the ParseError-string round
|
|
trip. Unlocks better hint UX.
|
|
4. **`HintMode` annotations** (§4). Mechanical migration of
|
|
the ad-hoc hint cases in `input_render.rs` to node
|
|
annotations. Small.
|
|
5. **Ranker hook** (§5). Future work. Plug-in point for
|
|
frequency-based ranking, content-aware priors, recency.
|
|
|
|
After (1) and (2) land, the codebase reaches the steady-state
|
|
ADR-0024 envisioned: one declaration per command, no scatter,
|
|
walker as single source of truth across parse / complete /
|
|
highlight / hint / usage.
|
|
|
|
## ADR index (read these before touching the related areas)
|
|
|
|
```
|
|
0000 Record architecture decisions (process)
|
|
0001 Language and TUI framework (Rust + Ratatui)
|
|
— chumsky dependency dropped in Phase F minimal
|
|
0002 Database engine (User-facing posture)
|
|
0003 Input modes and command dispatch
|
|
0004 Project file format (amended by 0015)
|
|
0005 Column type vocabulary
|
|
0006 Undo snapshots and replay log (designed, not impl)
|
|
0007 Sharing and export (amended by 0015 amendment 1)
|
|
0008 Testing approach
|
|
0009 DSL command syntax conventions
|
|
0010 Database access via worker thread
|
|
0011 FK column type compatibility
|
|
0012 Internal metadata for user-facing column types
|
|
0013 Relationships, naming, and rebuild-table strategy
|
|
0014 Data operations, value literals, and auto-show
|
|
0015 Project storage runtime
|
|
0016 Pretty table rendering for data and structure views
|
|
0017 Column type-change compatibility
|
|
0018 Auto-fill contracts for serial and shortid columns
|
|
0019 Friendly error layer (H1) and i18n message catalog
|
|
— `parse.token.keyword.*` collapse pending Phase F full
|
|
0020 Tokenization layer for the DSL parser
|
|
— superseded by the scannerless walker in ADR-0024;
|
|
the lexer module survives until Phase F full
|
|
0021 Parser-as-source-of-truth for H1a
|
|
— usage info migration to grammar nodes pending
|
|
(CommandNode.usage_id declared, not consumed yet)
|
|
0022 Ambient typing assistance (I3 + I4 unified)
|
|
— completion still chumsky-bridge; walker direct
|
|
path pending
|
|
0023 Unified declarative grammar tree (direction)
|
|
— superseded for execution by ADR-0024
|
|
0024 Unified grammar tree: execution plan (ACCEPTED)
|
|
— A through F minimal landed; F full + D full deferred
|
|
```
|
|
|
|
## Repository layout (delta vs. handoff-8)
|
|
|
|
New files this session:
|
|
|
|
```
|
|
src/dsl/grammar/
|
|
mod.rs (267) — Node enum, Word, IdentSource, HintMode,
|
|
HighlightClass, ValidationError,
|
|
IdentValidator, NumberValidator,
|
|
CommandNode, REGISTRY (20 commands)
|
|
app.rs (272) — 11 app-lifecycle commands
|
|
ddl.rs (492) — drop, add, rename, change, create
|
|
data.rs (504) — show, insert, update, delete, replay
|
|
shared.rs (108) — type validator, qualified_column,
|
|
referential_clauses, action_keyword
|
|
src/dsl/walker/
|
|
mod.rs (~620) — walk() entry + 53 walker tests
|
|
driver.rs (~570) — per-node-kind dispatch with backtracking
|
|
context.rs (43) — WalkContext (schema fields stubbed)
|
|
outcome.rs (~165) — WalkResult, WalkOutcome, MatchedPath
|
|
lex_helpers.rs (~190) — byte-level helpers (skip_whitespace,
|
|
consume_ident, match_keyword,
|
|
consume_bare_path, consume_flag,
|
|
consume_number_literal, consume_string_literal)
|
|
```
|
|
|
|
Files modified this session:
|
|
|
|
```
|
|
src/dsl/mod.rs — wire grammar + walker modules
|
|
src/dsl/parser.rs — chumsky combinators deleted; now a
|
|
~290-line walker bridge with the
|
|
original test suite (~840 lines) intact
|
|
Cargo.toml — chumsky dependency removed
|
|
Cargo.lock — regenerated
|
|
```
|
|
|
|
Files NOT touched but worth knowing about (still consume the
|
|
legacy modules per §1):
|
|
|
|
```
|
|
src/dsl/lexer.rs — still exports lex() / Token / TokenKind
|
|
src/dsl/keyword.rs — Keyword enum still alive
|
|
src/dsl/ident_slot.rs — IdentSlot enum still alive
|
|
src/dsl/usage.rs — REGISTRY + matched_entry still alive
|
|
src/completion.rs — reads ParseError::Invalid::expected
|
|
(bridge from walker), uses Keyword +
|
|
IdentSlot
|
|
src/input_render.rs — uses lex() for token-class colouring
|
|
src/theme.rs — token colour mappings keyed on Keyword
|
|
src/runtime.rs, src/db.rs — IdentSlot::expected_label round-trip
|
|
```
|
|
|
|
## How to take over
|
|
|
|
1. **Read this file.**
|
|
2. **Read `CLAUDE.md`** for the working-style rules. Note
|
|
the "Escalate ambiguity — do not decide for the user"
|
|
rule. The deferred items below are scoped enough that
|
|
most decisions are clear; escalate if the spec genuinely
|
|
disagrees with the implementation.
|
|
3. **Read ADR-0024**
|
|
(`docs/adr/0024-unified-grammar-tree-execution-plan.md`)
|
|
to understand the design intent. Phase F (full) and Phase D
|
|
(full) are the unfinished work.
|
|
4. **Skim `src/dsl/grammar/mod.rs`** — the Node enum + Word
|
|
+ CommandNode + REGISTRY are the contract.
|
|
5. **Skim `src/dsl/walker/mod.rs`** — the walk() entry +
|
|
bridge logic. The 53 tests at the bottom of that file
|
|
are the behavioural spec.
|
|
6. **Run `cargo test`** to confirm the 844-test baseline.
|
|
Lib test count is 711 in `rdbms_playground` (the rest are
|
|
integration + doctests). Total should be 844 passed, 0
|
|
failed, 1 ignored.
|
|
7. **Run `cargo clippy --all-targets -- -D warnings`** to
|
|
confirm clean baseline.
|
|
8. **Pick a deferred item from §1-§5** and start. §1
|
|
(Phase F full) is the natural next move; it unblocks §3
|
|
(walker-driven completion) and §4 (HintMode annotations).
|
|
§2 (Phase D full) is the second-largest item and can
|
|
land in parallel since it touches the grammar layer
|
|
rather than the consumer layer.
|
|
|
|
### End-to-end smoke test (current state, post-ADR-0024)
|
|
|
|
```
|
|
$ rm -rf /tmp/handoff9-smoke
|
|
$ rdbms-playground --data-dir /tmp/handoff9-smoke
|
|
|
|
# Inside the app:
|
|
|
|
# All commands now route through the walker. User-visible
|
|
# behaviour is unchanged from handoff-8 except for:
|
|
# - import / export paths with spaces require quotes
|
|
# - replay paths with spaces require quotes
|
|
# - parse error wording uses "after `<consumed>`, expected …,
|
|
# found end of input" framing (matches the chumsky-era
|
|
# contract; tests pin this down)
|
|
|
|
# Smoke commands:
|
|
help -- in-app help
|
|
mode advanced -- mode switch
|
|
quit -- exit (no `q`)
|
|
:quit -- one-shot escape
|
|
also works
|
|
|
|
# DDL:
|
|
create table Customers with pk
|
|
add column Customers: Email (text)
|
|
add 1:n relationship from Customers.id to Orders.customer_id
|
|
|
|
# Data:
|
|
insert into Customers values (1, 'Alice')
|
|
update Customers set Email='new@b.c' where id=1
|
|
delete from Customers where id=1
|
|
show data Customers
|
|
show table Customers
|
|
|
|
# Replay:
|
|
replay history.log
|
|
replay 'my project/seed.commands'
|
|
|
|
# Errors:
|
|
frobulate widgets
|
|
# → expected one of `add`, `change`, `create`, `delete`,
|
|
# `drop`, `export`, `help`, `import`, `insert`, `load`,
|
|
# `messages`, `mode`, `new`, `quit`, `rebuild`, `rename`,
|
|
# `replay`, `save`, `show`, or `update`, found `frobulate`
|
|
|
|
mode bogus
|
|
# → unknown mode 'bogus' (expected 'simple' or 'advanced')
|
|
|
|
change column T: c (int) --force-conversion --dont-convert
|
|
# → `--force-conversion` and `--dont-convert` are mutually
|
|
# exclusive — pick one.
|
|
|
|
create table Customers
|
|
# → tables need at least one column. Add `with pk` for a
|
|
# default `id INTEGER PRIMARY KEY`, or `with pk <name>:<type>`
|
|
# …
|
|
|
|
# All wording sourced from the en-US.yaml catalog via the
|
|
# walker's ValidationError catalog-key mechanism.
|
|
```
|
|
|
|
After Phase F full lands, this smoke test extends with:
|
|
- Per-keystroke highlighting driven by walker `per_byte_class`
|
|
- Cursor-position completion driven by walker direct path
|
|
- Usage templates rendered from `CommandNode.usage_id`
|
|
- Lexer/Keyword/IdentSlot modules removed from the source tree
|