Compare commits
3 Commits
v0.1.0
...
1660a6a17c
| Author | SHA1 | Date | |
|---|---|---|---|
| 1660a6a17c | |||
| 5a37437055 | |||
| b4441507e2 |
@@ -37,9 +37,9 @@ Current decisions at a glance (each backed by an ADR):
|
|||||||
simple to advanced (ADR-0003). No other sigils.
|
simple to advanced (ADR-0003). No other sigils.
|
||||||
- **Project format:** `project.yaml` + `data/<table>.csv` +
|
- **Project format:** `project.yaml` + `data/<table>.csv` +
|
||||||
`history.log`; `playground.db` is a derived artifact (ADR-0004,
|
`history.log`; `playground.db` is a derived artifact (ADR-0004,
|
||||||
amended by ADR-0015). Implemented through Iteration 4 +
|
amended by ADR-0015). Fully implemented (ADR-0015 Iterations
|
||||||
cleanup; export/import (Iter 5) and migration framework /
|
1–6): export/import, `--resume`, persistent input history, and
|
||||||
--resume / persistent input history (Iter 6) pending.
|
the migration framework scaffold are all done.
|
||||||
- **Project storage runtime:** every command persists through to
|
- **Project storage runtime:** every command persists through to
|
||||||
db + yaml + csv + history.log in one execution context, gated
|
db + yaml + csv + history.log in one execution context, gated
|
||||||
by the combined db persistence logic; commit-db-last ordering
|
by the combined db persistence logic; commit-db-last ordering
|
||||||
@@ -335,16 +335,8 @@ all of `target/`, forcing a full from-scratch rebuild).
|
|||||||
These are explicitly tracked (mostly in `requirements.md`) but
|
These are explicitly tracked (mostly in `requirements.md`) but
|
||||||
not yet implemented:
|
not yet implemented:
|
||||||
|
|
||||||
- **Project storage** (track 2): largely implemented through
|
|
||||||
Iteration 4 + cleanup pass + safety hardening (Iterations
|
|
||||||
1–4 of ADR-0015). Pending pieces: `export` / `import` (Iter
|
|
||||||
5), `--resume` + persistent input history hydration +
|
|
||||||
migration framework scaffold (Iter 6).
|
|
||||||
- **Modify relationship** (C3a): drop+add covers the use case
|
- **Modify relationship** (C3a): drop+add covers the use case
|
||||||
today.
|
today.
|
||||||
- **m:n convenience** (C4): auto-generates a junction table
|
|
||||||
with appropriate FKs — depends on relationships being solid
|
|
||||||
(they are).
|
|
||||||
- **Strong syntax-help in parse errors** (H1a): point users at
|
- **Strong syntax-help in parse errors** (H1a): point users at
|
||||||
missing keywords/clauses rather than the unexpected
|
missing keywords/clauses rather than the unexpected
|
||||||
character. *(H1 — the friendly **database**-error layer — is
|
character. *(H1 — the friendly **database**-error layer — is
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# Session handoff — 2026-06-15 (71)
|
||||||
|
|
||||||
|
Short, focused handover. Continues immediately from handoff-70 (which
|
||||||
|
shipped H2 / the contextual `hint`, ADR-0053). **A user smoke-test
|
||||||
|
surfaced a correctness bug in the hint content, and it implicates the
|
||||||
|
whole corpus.** This handoff exists so the next session does a
|
||||||
|
**systematic semantic verification pass over every hint block** — context
|
||||||
|
ran too low to do it now.
|
||||||
|
|
||||||
|
## §1. State
|
||||||
|
|
||||||
|
**Branch:** `main`, clean, all committed (local; push pending). **2499
|
||||||
|
pass / 1 ignored, clippy clean.** Open issues: #35–#38 (see handoff-70).
|
||||||
|
H2 / ADR-0053 is *functionally* complete; the **content is not
|
||||||
|
trustworthy** until the pass below is done.
|
||||||
|
|
||||||
|
## §2. The bug (confirmed)
|
||||||
|
|
||||||
|
`hint.cmd.create_table` (in `src/friendly/strings/en-US.yaml`) reads:
|
||||||
|
|
||||||
|
```
|
||||||
|
What: Create a new table — its columns, their types, and a primary key.
|
||||||
|
Example: create table Customers with pk id(serial), name(text), email(text)
|
||||||
|
Concept: A table is a set of rows that share the same columns. The primary
|
||||||
|
key uniquely identifies each row; a `serial` key numbers the rows for you.
|
||||||
|
```
|
||||||
|
|
||||||
|
**This is wrong.** In the DSL, **everything after `with pk` is the
|
||||||
|
primary-key column list** (a possibly *compound* PK, ADR-0005). So the
|
||||||
|
example does **not** create a table with `pk=id` plus regular columns
|
||||||
|
`name`/`email` — it creates a table whose **compound primary key is
|
||||||
|
(id, name, email)**. Non-key columns are added *separately* with
|
||||||
|
`add column`. The `what` ("its columns, their types") and the example
|
||||||
|
both mislead a learner badly.
|
||||||
|
|
||||||
|
- **Evidence:** real test usage is `create table Orders with pk
|
||||||
|
id(serial), CustId(int)` (a 2-column *compound PK*) and the common form
|
||||||
|
`create table X with pk id(int)` (single-column PK only). The usage
|
||||||
|
template `create table <Name> with pk [<col>(<type>)[, ...]]` is itself
|
||||||
|
misleading — the `[, ...]` is the PK list, not regular columns.
|
||||||
|
- **Correct mental model:** `create table <T> with pk <pk-cols…>` then
|
||||||
|
`add column <T>: <name> (<type>)` for each non-key column. Confirm
|
||||||
|
against ADR-0005 (compound PK) and ADR-0009 (DSL syntax) when fixing.
|
||||||
|
|
||||||
|
## §3. Root cause — why this needs a *full* pass
|
||||||
|
|
||||||
|
During Phase C I verified *some* examples against `parse.usage.*`
|
||||||
|
templates and real test greps, but for others I **extrapolated** beyond
|
||||||
|
verified syntax. For `create_table` I saw `... with pk id(int)` (single
|
||||||
|
col) and wrongly generalised to "pk + more columns," misreading the
|
||||||
|
`with pk` list as a column list. The examples are **syntactically**
|
||||||
|
checked but not **semantically** — i.e. not verified to *do what the
|
||||||
|
`what`/`concept` claims*.
|
||||||
|
|
||||||
|
So the corpus needs a pass that, for **every** `hint.cmd.*` and
|
||||||
|
`hint.err.*` block, checks:
|
||||||
|
1. the `example` parses **and runs**, and
|
||||||
|
2. it actually demonstrates what `what`/`concept` says, and
|
||||||
|
3. `what`/`concept` are factually true of the real behaviour.
|
||||||
|
|
||||||
|
**Don't trust grep+extrapolation.** Prefer: run the example in the app
|
||||||
|
(or a Tier-3 test), or check it against the authoritative ADR.
|
||||||
|
|
||||||
|
## §4. The pass — how to do it (next session)
|
||||||
|
|
||||||
|
The corpus lives in `src/friendly/strings/en-US.yaml` under `hint.cmd.*`
|
||||||
|
(per command form) and `hint.err.*` (per runtime error class). The
|
||||||
|
inventory and authoritative syntax sources:
|
||||||
|
|
||||||
|
- **`hint.cmd.<form>`** — for each, cross-check the example against the
|
||||||
|
matching `parse.usage.<form>` template **and** the form's ADR, and run
|
||||||
|
it. Highest-risk (extrapolated, verify first): **DDL** — `create_table`
|
||||||
|
(known wrong), `add_column`, `add_index`, `add_constraint`,
|
||||||
|
`change_column`, `drop_*`, `create_m2n`; **advanced-SQL** — confirm
|
||||||
|
each is in the supported SQL subset (`select`, `with` CTE,
|
||||||
|
`sql_insert/update/delete`, `sql_create_table`, `sql_alter_table`,
|
||||||
|
`sql_create_index/drop_index/drop_table`, `explain_sql`); **DML** —
|
||||||
|
`seed` forms, `explain`, `show_*`, `update`/`delete` (`--all-rows` /
|
||||||
|
required-WHERE wording). App commands are lower-risk (reference-style).
|
||||||
|
- **`hint.err.<class>`** — verify the fix recipe in `example` is actually
|
||||||
|
the right remedy and `concept` matches the engine's real behaviour
|
||||||
|
(FK sides, `on delete` actions, check/not_null/unique semantics).
|
||||||
|
- Relevant ADRs: 0005 (types + compound PK), 0009 (DSL syntax), 0011 (FK
|
||||||
|
type compat), 0013 (relationships/rebuild), 0014 (data ops +
|
||||||
|
required-WHERE), 0025 (indexes), 0028/0039 (explain), 0030–0036 (SQL
|
||||||
|
subset), 0048 (seed). `docs/requirements.md` for scope.
|
||||||
|
|
||||||
|
**Suggested method:** drive the app (`/run` or a small PTY/Tier-3 harness)
|
||||||
|
and actually execute each example; or add a test that parses+runs every
|
||||||
|
`hint.cmd.*` example and asserts success. The latter would also be a
|
||||||
|
durable regression guard — consider adding it as part of the pass (it
|
||||||
|
upgrades the comprehensiveness coverage test from "a block exists" to
|
||||||
|
"the example actually works").
|
||||||
|
|
||||||
|
## §5. Immediate fix ready to apply
|
||||||
|
|
||||||
|
`create_table` is diagnosed (§2). The corrected block should make the
|
||||||
|
example a PK-only `create table` and move the regular columns to a
|
||||||
|
follow-up `add column`, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
What: Create a new table with its primary key.
|
||||||
|
Example: create table Customers with pk id(serial)
|
||||||
|
Concept: A table is a set of rows sharing the same columns. `with pk`
|
||||||
|
declares the primary key (one column, or several for a compound
|
||||||
|
key); add the other columns afterwards with `add column`.
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply this (and re-check `create_m2n` / `add_*` while there), but only as
|
||||||
|
part of the systematic pass — a one-off fix risks leaving siblings wrong.
|
||||||
|
|
||||||
|
## §6. How to take over
|
||||||
|
|
||||||
|
1. Read handoffs 70 → 71, `CLAUDE.md`.
|
||||||
|
2. Confirm green: `cargo test` (2499 / 1 ignored), `cargo clippy
|
||||||
|
--all-targets`.
|
||||||
|
3. Do the §4 pass (consider the run-every-example test in §4). Test-first,
|
||||||
|
`/runda` before commit, confirm the commit message with the user.
|
||||||
|
4. Pedagogy wins — these are teaching strings; correctness and clarity
|
||||||
|
over cleverness.
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# Session handoff — 2026-06-15 (72)
|
||||||
|
|
||||||
|
Short, focused handover. Continues from handoff-71, which asked the next
|
||||||
|
session to run a **systematic semantic verification pass over every
|
||||||
|
`hint` block** (handoff-70 shipped H2 / ADR-0053, but a user smoke-test
|
||||||
|
found a wrong hint and implicated the whole corpus). **That pass is now
|
||||||
|
done.** Four content errors fixed, a durable parse-guard added, two stale
|
||||||
|
docs corrected. Commit `5a37437`.
|
||||||
|
|
||||||
|
## §1. State
|
||||||
|
|
||||||
|
**Branch:** `main`, clean, all committed (local; **push pending** — your
|
||||||
|
step). **2500 pass / 0 fail / 1 ignored** (the long-standing `friendly`
|
||||||
|
doctest), **clippy clean** (nursery, all targets). The +1 vs handoff-71's
|
||||||
|
2499 is the new guard test. Open Gitea issues unchanged: **#35–#38**.
|
||||||
|
|
||||||
|
## §2. The verification pass (commit `5a37437`)
|
||||||
|
|
||||||
|
Method: cross-checked every `hint.cmd.*` example against its
|
||||||
|
`parse.usage.*` template, ground-truthed every concept claim against the
|
||||||
|
authoritative ADR **and a named existing test** (not grep+extrapolation —
|
||||||
|
the trap handoff-71 §3 warned about), and parse-validated all 49 command
|
||||||
|
examples via a new guard.
|
||||||
|
|
||||||
|
### Four content errors fixed (`src/friendly/strings/en-US.yaml`)
|
||||||
|
|
||||||
|
| Block | Bug | Fix |
|
||||||
|
|---|---|---|
|
||||||
|
| `cmd.create_table` | Example `with pk id(serial), name(text), email(text)` declares a **3-column compound PK**, not a PK + regular columns. Every `with pk` column is a key member — confirmed by the grammar test comment *"Every `create table` column is a primary-key column"* (`ddl.rs`), ADR-0005. | Single-column PK + `add column` for the rest; `what`/`concept` aligned. |
|
||||||
|
| `cmd.save` | `save as my-shop` **does not parse** — `build_save` yields `AppCommand::SaveAs` with **no inline name**; `save as` opens a path-entry modal (`iteration4b` tests). | Example → `save as`; `what` de-implied; added an accurate temp-vs-named-auto-save `concept`. |
|
||||||
|
| `cmd.import` | Target `shop-copy` **does not parse** — the `as <target>` slot is an `IdentSource::NewName` ident that tokenises only up to the hyphen. (The zip path is a BarePath and *does* accept hyphens, hence `export my-shop.zip` is fine.) | → `shop_copy`. |
|
||||||
|
| `err.foreign_key.child_side.concept` | Offered `on delete set null/cascade` as the remedy — but `error_hint_class` maps child_side to **insert/update** violations; `on delete` governs the **parent** direction. The tier-1 hint (line 64) correctly omits it. | Corrected: parent must exist first; clarified `on delete` is the *other* direction. |
|
||||||
|
|
||||||
|
### Durable guard added
|
||||||
|
|
||||||
|
`every_cmd_hint_example_parses_in_its_mode` (`src/dsl/grammar/mod.rs`,
|
||||||
|
in the `hint_key_tests` module). **Catalog-driven** — it iterates
|
||||||
|
`catalog().keys()` for `hint.cmd.*.example` rather than the REGISTRY, so
|
||||||
|
an orphaned/mis-keyed block can't slip past; floor-asserts ≥49 examples.
|
||||||
|
Each parses in its taught mode (advanced for the SQL surface, simple
|
||||||
|
otherwise). It caught the `save` and `import` errors **test-first** (red
|
||||||
|
before the YAML fix). Registered the new `hint.cmd.save.concept` key in
|
||||||
|
`keys.rs` (the `keys_validate_against_catalog` test requires every catalog
|
||||||
|
key be declared).
|
||||||
|
|
||||||
|
### Verified correct (not changed)
|
||||||
|
|
||||||
|
All other `cmd`/`err` blocks. Notably the guard-*concept* claims were each
|
||||||
|
confirmed against a named runtime test, not assumed:
|
||||||
|
`drop_column_refuses_primary_key` / `…_column_in_a_relationship`,
|
||||||
|
`drop_table_with_inbound_relationship_errors`,
|
||||||
|
`add_not_null_column_without_default_to_populated_table_is_refused`. The
|
||||||
|
corrected `create_table` story stays coherent with the `Customers`-
|
||||||
|
referencing examples (id serial PK → `add column` name/email → `insert`
|
||||||
|
skips the auto id).
|
||||||
|
|
||||||
|
## §3. Docs corrected (same commit)
|
||||||
|
|
||||||
|
Discovered while verifying `create_m2n` (which **is** implemented —
|
||||||
|
`db.rs::do_create_m2n_relationship` + `tests/it/m2n.rs`):
|
||||||
|
|
||||||
|
- **CLAUDE.md** carried two **stale "deferred" claims**, both already
|
||||||
|
implemented. Removed/updated: (a) the at-a-glance project-format line
|
||||||
|
said export/import (Iter 5) + `--resume`/input-history/migration (Iter
|
||||||
|
6) were "pending" — all `[x]` in `requirements.md` (ADR-0015); (b) the
|
||||||
|
"Things deliberately deferred" list still had the **m:n convenience
|
||||||
|
(C4)** bullet and the same project-storage bullet. `requirements.md`
|
||||||
|
was already correct (C4 done 2026-06-10, ADR-0045), so only a
|
||||||
|
verification-pass note was appended to its **H2** entry.
|
||||||
|
|
||||||
|
## §4. Scope note — what the guard does *not* do
|
||||||
|
|
||||||
|
The bug class here is **semantic** (an example that parses and runs but
|
||||||
|
misrepresents the prose — e.g. `create_table`). The guard enforces only
|
||||||
|
the **syntactic floor**: examples parse in their mode. It backstops
|
||||||
|
future typos/clause-drift but cannot police meaning. Semantic correctness
|
||||||
|
of the current corpus rests on this session's review (recorded in the
|
||||||
|
commit + requirements.md H2). A stronger-but-brittler option was offered
|
||||||
|
to the user and **not built pending their call**: per-form assertions
|
||||||
|
that each example resolves to the *expected command shape* (e.g.
|
||||||
|
create_table → single-column PK). `hint.err.*` examples are fix-recipe
|
||||||
|
prose, not runnable, so they're verified by review only — inherent.
|
||||||
|
|
||||||
|
## §5. Next session — start here
|
||||||
|
|
||||||
|
The hint corpus is now trustworthy. Open roadmap (verify against the CI
|
||||||
|
merge first, per handoff-70 §5):
|
||||||
|
|
||||||
|
1. **Push** (your step) — this commit + the still-unpushed backlog from
|
||||||
|
handoffs 70/71 (the CI merge + all of H2).
|
||||||
|
2. **#35 (cargo fmt gate)** — the natural pairing with the merged CI; the
|
||||||
|
user wanted it done once, before first publication. The tree is **not**
|
||||||
|
fmt-clean (~1800 pre-existing diffs).
|
||||||
|
3. Other `requirements.md` open items: **TT4** PTY tier-4 (unwired),
|
||||||
|
**I1** multi-line input, **I5/B3** in-flight cancellation, **V4**
|
||||||
|
session journal (own ADR), **TU1** tutorial system (own ADR).
|
||||||
|
4. Hint follow-ups if wanted: **#37** clause-concept hints, **#38**
|
||||||
|
diagnostic route + `diagnostic.*` blocks, **#36** `help` advanced-SQL.
|
||||||
|
|
||||||
|
## §6. How to take over
|
||||||
|
|
||||||
|
1. Read handoffs 70 → 71 → 72, `CLAUDE.md`, `docs/requirements.md`.
|
||||||
|
2. Confirm green: `cargo test` (**2500 / 1 ignored**) + `cargo clippy
|
||||||
|
--all-targets` (clean).
|
||||||
|
3. For anything in the `hint` area, read **ADR-0053** first. For the
|
||||||
|
corpus, `src/friendly/strings/en-US.yaml` (`hint.cmd.*` / `hint.err.*`)
|
||||||
|
is the content; the guard in `src/dsl/grammar/mod.rs` is the regression
|
||||||
|
net.
|
||||||
|
4. Workflow unchanged: phased, test-first, `/runda` + DA before commits,
|
||||||
|
ADR amendment + README index-upkeep for decided-area changes, confirm
|
||||||
|
commit messages with the user.
|
||||||
|
5. Consider a `cargo sweep` at this milestone (`target/` grows; see
|
||||||
|
CLAUDE.md "Build hygiene").
|
||||||
@@ -820,7 +820,12 @@ since ADR-0027.)
|
|||||||
(`what`/`example`/`concept`) covers every command form + the 9 runtime
|
(`what`/`example`/`concept`) covers every command form + the 9 runtime
|
||||||
error classes, enforced by a comprehensiveness coverage test. Deferred:
|
error classes, enforced by a comprehensiveness coverage test. Deferred:
|
||||||
the pre-submit-diagnostic route + `diagnostic.*` blocks (#38),
|
the pre-submit-diagnostic route + `diagnostic.*` blocks (#38),
|
||||||
clause-concept hints (#37).)*
|
clause-concept hints (#37). **Content verified 2026-06-15 (handoff-71):**
|
||||||
|
a semantic pass over every `hint.cmd.*`/`hint.err.*` block fixed four
|
||||||
|
errors — `create_table` (compound-PK misread), `save` (no inline name),
|
||||||
|
`import` (hyphen-rejecting target), and `foreign_key.child_side` (wrong
|
||||||
|
`on delete` remedy) — and added a catalogue-driven guard test that parses
|
||||||
|
every command example in its taught mode.)*
|
||||||
- [x] **H3** `help` provides general reference and per-command
|
- [x] **H3** `help` provides general reference and per-command
|
||||||
help.
|
help.
|
||||||
*(Done 2026-06-07: the **general reference** is `help` (no arg) —
|
*(Done 2026-06-07: the **general reference** is `help` (no arg) —
|
||||||
|
|||||||
@@ -1012,6 +1012,75 @@ mod hint_key_tests {
|
|||||||
assert!(cat.get(&key).is_some(), "missing tier-3 error block `{key}`");
|
assert!(cat.get(&key).is_some(), "missing tier-3 error block `{key}`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Semantic-verification guard (handoff-71): every `hint.cmd.<form>`
|
||||||
|
/// **example** must parse in the mode the form is taught for. This
|
||||||
|
/// backstops the bug class found in the H2 corpus pass — an example
|
||||||
|
/// that drifts out of the real grammar (a typo, a removed clause, or
|
||||||
|
/// an argument the command never accepted, e.g. an inline name on
|
||||||
|
/// `save as` which opens a modal instead). It cannot police the
|
||||||
|
/// *semantics* of an example that happens to parse (that is the
|
||||||
|
/// manual pass), but it locks the syntactic floor so future edits
|
||||||
|
/// can't ship an unparseable teaching line.
|
||||||
|
///
|
||||||
|
/// The mode per form mirrors `hint_key_for_input_in_mode`: the
|
||||||
|
/// advanced-SQL forms are taught in advanced mode; everything else
|
||||||
|
/// (DSL + app commands) in simple mode.
|
||||||
|
#[test]
|
||||||
|
fn every_cmd_hint_example_parses_in_its_mode() {
|
||||||
|
use crate::dsl::parser::parse_command_in_mode;
|
||||||
|
use crate::mode::Mode;
|
||||||
|
|
||||||
|
// Advanced-mode forms — the SQL surface (ADR-0030–0039). Every
|
||||||
|
// other form (DSL + app commands) is taught in simple mode. This
|
||||||
|
// mirrors the mode split `hint_key_for_input_in_mode` resolves.
|
||||||
|
const ADVANCED: &[&str] = &[
|
||||||
|
"sql_create_table",
|
||||||
|
"sql_alter_table",
|
||||||
|
"sql_create_index",
|
||||||
|
"sql_drop_index",
|
||||||
|
"sql_drop_table",
|
||||||
|
"sql_insert",
|
||||||
|
"sql_update",
|
||||||
|
"sql_delete",
|
||||||
|
"select",
|
||||||
|
"with",
|
||||||
|
"explain_sql",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Iterate the *catalog* (the corpus is the source of truth), not the
|
||||||
|
// REGISTRY: this reaches every `hint.cmd.<id>` block including any
|
||||||
|
// not owned by a command node, so an orphaned or mis-keyed example
|
||||||
|
// can't slip past the guard.
|
||||||
|
let cat = crate::friendly::catalog();
|
||||||
|
let mut checked = 0usize;
|
||||||
|
for key in cat.keys() {
|
||||||
|
let Some(id) = key
|
||||||
|
.strip_prefix("hint.cmd.")
|
||||||
|
.and_then(|rest| rest.strip_suffix(".example"))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let example = cat.get(key).expect("key came from the catalog");
|
||||||
|
let mode = if ADVANCED.contains(&id) {
|
||||||
|
Mode::Advanced
|
||||||
|
} else {
|
||||||
|
Mode::Simple
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
parse_command_in_mode(example, mode).is_ok(),
|
||||||
|
"hint.cmd.{id}.example does not parse in {mode:?} mode: {example:?}",
|
||||||
|
);
|
||||||
|
checked += 1;
|
||||||
|
}
|
||||||
|
// Floor guard: the corpus had 49 command forms at the time of
|
||||||
|
// writing (ADR-0053). If this drops, a block (and its example
|
||||||
|
// coverage) silently vanished.
|
||||||
|
assert!(
|
||||||
|
checked >= 49,
|
||||||
|
"expected at least 49 hint.cmd.* examples, checked {checked}",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("hint.cmd.rebuild.concept", &[]),
|
("hint.cmd.rebuild.concept", &[]),
|
||||||
("hint.cmd.save.what", &[]),
|
("hint.cmd.save.what", &[]),
|
||||||
("hint.cmd.save.example", &[]),
|
("hint.cmd.save.example", &[]),
|
||||||
|
("hint.cmd.save.concept", &[]),
|
||||||
("hint.cmd.new.what", &[]),
|
("hint.cmd.new.what", &[]),
|
||||||
("hint.cmd.new.example", &[]),
|
("hint.cmd.new.example", &[]),
|
||||||
("hint.cmd.load.what", &[]),
|
("hint.cmd.load.what", &[]),
|
||||||
|
|||||||
@@ -430,8 +430,9 @@ hint:
|
|||||||
example: "rebuild"
|
example: "rebuild"
|
||||||
concept: "The text files (project.yaml + the data folder) are the source of truth; the database is derived and can always be rebuilt from them."
|
concept: "The text files (project.yaml + the data folder) are the source of truth; the database is derived and can always be rebuilt from them."
|
||||||
save:
|
save:
|
||||||
what: "Save the current project under a name; `save as` copies it to a new one."
|
what: "Save the current project; `save as` copies it to a new name or location."
|
||||||
example: "save as my-shop"
|
example: "save as"
|
||||||
|
concept: "On a temporary project, `save` opens a prompt to give it a permanent name; a named project auto-saves as you work, so `save` on one is already done. `save as` always prompts for a new name or path — use it to copy a project."
|
||||||
new:
|
new:
|
||||||
what: "Close the current project and start a fresh temporary one."
|
what: "Close the current project and start a fresh temporary one."
|
||||||
example: "new"
|
example: "new"
|
||||||
@@ -444,7 +445,7 @@ hint:
|
|||||||
concept: "The zip carries the schema and data as text, so anyone can rebuild the very same database from it."
|
concept: "The zip carries the schema and data as text, so anyone can rebuild the very same database from it."
|
||||||
import:
|
import:
|
||||||
what: "Unpack a project zip into a new project and switch to it."
|
what: "Unpack a project zip into a new project and switch to it."
|
||||||
example: "import my-shop.zip as shop-copy"
|
example: "import my-shop.zip as shop_copy"
|
||||||
mode:
|
mode:
|
||||||
what: "Switch between simple mode (the guided teaching commands) and advanced mode (raw SQL)."
|
what: "Switch between simple mode (the guided teaching commands) and advanced mode (raw SQL)."
|
||||||
example: "mode advanced"
|
example: "mode advanced"
|
||||||
@@ -465,9 +466,9 @@ hint:
|
|||||||
example: "copy last"
|
example: "copy last"
|
||||||
# DDL — schema-shaping commands (Phase C batch 2).
|
# DDL — schema-shaping commands (Phase C batch 2).
|
||||||
create_table:
|
create_table:
|
||||||
what: "Create a new table — its columns, their types, and a primary key."
|
what: "Create a new table and declare its primary key."
|
||||||
example: "create table Customers with pk id(serial), name(text), email(text)"
|
example: "create table Customers with pk id(serial)"
|
||||||
concept: "A table is a set of rows that share the same columns. The primary key uniquely identifies each row; a `serial` key numbers the rows for you."
|
concept: "A table is a set of rows sharing the same columns. `with pk` declares the primary key — one column, or several for a compound key; add the other columns afterwards with `add column`. A `serial` key numbers the rows for you."
|
||||||
create_m2n:
|
create_m2n:
|
||||||
what: "Create a junction table linking two tables many-to-many."
|
what: "Create a junction table linking two tables many-to-many."
|
||||||
example: "create m:n relationship from Students to Courses"
|
example: "create m:n relationship from Students to Courses"
|
||||||
@@ -606,7 +607,7 @@ hint:
|
|||||||
child_side:
|
child_side:
|
||||||
what: "The value you gave for the child column doesn't match any parent row, so the foreign key has nothing to point at."
|
what: "The value you gave for the child column doesn't match any parent row, so the foreign key has nothing to point at."
|
||||||
example: "First insert the parent (insert into Customers …), then the child that references it."
|
example: "First insert the parent (insert into Customers …), then the child that references it."
|
||||||
concept: "A foreign key is a promise that every child points at a real parent, so the parent must exist first. To allow orphans on delete instead, set the relationship's `on delete` to `set null` or `cascade`."
|
concept: "A foreign key is a promise that every child points at a real parent, so the parent must exist before a child can reference it. (`on delete` actions like `cascade` or `set null` govern the other direction — what happens to children when their parent is removed — not this one.)"
|
||||||
parent_side:
|
parent_side:
|
||||||
what: "You're deleting or changing a row that other rows point at, which would orphan those children."
|
what: "You're deleting or changing a row that other rows point at, which would orphan those children."
|
||||||
example: "Delete the child rows first, or set the relationship's `on delete` to `cascade` (remove them too) or `set null` (keep them, unlinked)."
|
example: "Delete the child rows first, or set the relationship's `on delete` to `cascade` (remove them too) or `set null` (keep them, unlinked)."
|
||||||
|
|||||||
Reference in New Issue
Block a user