feat(render): incidental-DDL confirmations show structure only, no relationships (#28)
Per ADR-0050 (closing issue #28): the confirmation echo after an incidental structural edit — create table, add/drop/rename/change column, add/drop index — now renders the structure only (header + column box + indexes + constraints) and no longer appends the References:/Referenced by: relationship block. Rationale: a confirmation reports the change just made, not the table's relationships, which the user didn't touch. Relationship info is still one `show table <T>` away, and the relationship-subject surfaces (show table, add/drop relationship) keep their ADR-0044 diagrams unchanged. Scope is all incidental DDL (user-confirmed). Mechanism: drop the relationship-block call from render_structure (all its callers are incidental DDL); the handle_dsl_success diagram-vs-structure routing is unchanged. The orphaned relationship_prose_lines + cols_disp helpers are deleted (the prose format survives in ADR-0016 §5 + git history for a future OOS-7 always-prose setting). ADR-0050 supersedes ADR-0044 §1's incidental-DDL prose clause and the relationship-block half of ADR-0016 §5 (both annotated). Tests: prose- presence unit test + snapshot removed; new unit test locks structure- only with inbound+outbound relationships present; the misnamed add- column integration test inverted + renamed. 2458 pass / 0 fail / 0 skip, clippy clean.
This commit is contained in:
@@ -197,6 +197,16 @@ Referenced by:
|
||||
The relationship sections retain today's plain-text format
|
||||
to leave room for the future relationship-rendering ADR.
|
||||
|
||||
> **Superseded.** ADR-0044 replaced this prose block with compact
|
||||
> diagrams on relationship-subject surfaces (`show table`,
|
||||
> `add`/`drop relationship`). **ADR-0050 (2026-06-12, issue #28)** then
|
||||
> removed the relationship block entirely from incidental-DDL structure
|
||||
> echoes (`create table`, `add`/`drop`/`rename`/`change column`,
|
||||
> `add`/`drop index`) — those render structure only — and **deleted the
|
||||
> prose renderer**. The `References:` / `Referenced by:` format above is
|
||||
> retained here as documentation/provenance should the OOS-7
|
||||
> always-prose display setting ever be built.
|
||||
|
||||
### 6. Theme integration
|
||||
|
||||
Theme colors apply to the box-drawing characters via the
|
||||
|
||||
@@ -103,6 +103,10 @@ Prose-retained surfaces (**unchanged** from ADR-0016 §5):
|
||||
`add`/`drop index` — keep the terse `References:` /
|
||||
`Referenced by:` prose. A simple `add column` on a heavily-related
|
||||
table should not print a wall of diagrams.
|
||||
*(**Superseded 2026-06-12 by ADR-0050** (issue #28): these incidental
|
||||
DDL echoes now render **structure only** — no relationship block at
|
||||
all, neither prose nor diagram. The prose renderer was deleted. The
|
||||
diagram surfaces below are unchanged.)*
|
||||
|
||||
So this **partially supersedes ADR-0016 §5**: the prose block is
|
||||
replaced by diagrams on the relationship-subject surfaces and
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# ADR-0050: Incidental-DDL confirmations omit relationship info (structure-only)
|
||||
|
||||
## Status
|
||||
|
||||
**Accepted + implemented 2026-06-12 (issue #28).** Closes Gitea **#28**.
|
||||
Both forks below were escalated to the user and user-chosen before any
|
||||
code was written; implemented test-first. **Supersedes** the
|
||||
incidental-DDL clause of **ADR-0044 §1** and the part of **ADR-0016 §5**
|
||||
that placed a relationship block under every structure echo. The
|
||||
diagram behaviour ADR-0044 introduced for relationship-subject surfaces
|
||||
is unchanged.
|
||||
|
||||
## Context
|
||||
|
||||
ADR-0016 §5 rendered a structure box followed by a plain-text
|
||||
`References:` / `Referenced by:` relationship block under **every**
|
||||
structure echo. ADR-0044 §1 split that by surface:
|
||||
|
||||
- **Relationship-subject surfaces** — `show table <T>`,
|
||||
`add 1:n relationship`, `drop relationship`, `show relationship <name>`
|
||||
— render relationships as compact **diagrams** (the user asked for, or
|
||||
acted on, a relationship).
|
||||
- **Incidental DDL auto-shows** — `create table`, `add`/`drop`/`rename`/
|
||||
`change column`, `add`/`drop index` — kept the terse **prose** block,
|
||||
with the rationale *"a simple `add column` on a heavily-related table
|
||||
should not print a wall of diagrams."*
|
||||
|
||||
Issue #28 reconsiders the deeper question ADR-0044 did not ask: should
|
||||
an incidental-DDL confirmation show relationship information **at all**?
|
||||
Owner preference: **no.** A confirmation echo should focus on the change
|
||||
just made — the new / updated structure — not re-print the table's
|
||||
relationships, which the user did not touch. The terse prose was the
|
||||
lesser of "prose vs diagram", but the right answer for these surfaces is
|
||||
**neither**.
|
||||
|
||||
## Decision
|
||||
|
||||
**Incidental-DDL confirmation echoes render the structure only** — the
|
||||
table-name header, the column / type / constraints box, the `Indexes:`
|
||||
section, and the constraint section — with **no relationship section**
|
||||
(neither prose nor diagram).
|
||||
|
||||
- **Scope: all incidental DDL** (user-chosen, over "just `add column`"):
|
||||
`create table`, `add column`, `drop column`, `rename column`,
|
||||
`change column`, `add index`, `drop index`. The rule is uniform — a
|
||||
structural edit confirms structure, never relationships. (For a
|
||||
freshly `create`d table the relationship section was empty anyway; the
|
||||
rule still applies for consistency of the mental model.)
|
||||
- **Relationship-subject surfaces are unchanged.** `show table`,
|
||||
`add`/`drop relationship`, and `show relationship <name>` still render
|
||||
diagrams. Relationships appear **only** when the user asks for them
|
||||
(`show table` / `show relationship`) or acts on one
|
||||
(`add`/`drop relationship`).
|
||||
- **No information is lost.** Anything dropped from an incidental echo is
|
||||
one `show table <T>` away.
|
||||
|
||||
### Mechanism
|
||||
|
||||
The `handle_dsl_success` routing (`app.rs`) is **unchanged**: it still
|
||||
sends relationship-subject commands to the diagram renderer and
|
||||
everything else to `render_structure`. The change is entirely inside
|
||||
`render_structure` (`output_render.rs`): it no longer appends the
|
||||
relationship block — `render_structure` = structure box + indexes +
|
||||
constraints. All of `render_structure`'s callers are incidental DDL
|
||||
(verified), so this single edit covers the whole scope with no
|
||||
per-command branching.
|
||||
|
||||
### Prose renderer disposition
|
||||
|
||||
The orphaned prose renderer (`relationship_prose_lines`, and its
|
||||
sole helper `cols_disp`) is **deleted** (user-chosen, over retaining it
|
||||
dormant). After this change no shipped surface renders the prose form,
|
||||
so keeping it would be dead code. The prose format remains documented in
|
||||
**ADR-0016 §5** and in git history; if ADR-0044's OOS-7 user-configurable
|
||||
"always-prose" display setting is ever built, it re-introduces the ~30
|
||||
lines from that provenance.
|
||||
|
||||
## Forks (all user-chosen)
|
||||
|
||||
- **Scope:** *all incidental DDL*, not just `add column` — the owner's
|
||||
rationale ("confirm the change, not untouched relationships") applies
|
||||
uniformly, gives a clean mental model, and is the simpler edit (remove
|
||||
one call vs a per-command flag).
|
||||
- **Prose renderer:** *delete* it — no dead code — over retaining a
|
||||
public, tested-but-uncalled renderer for the speculative OOS-7 setting.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Incidental confirmations are shorter and on-topic; a heavily-related
|
||||
table no longer prints a relationship wall after `add column`.
|
||||
- One relationship renderer (prose) leaves the codebase; the diagram
|
||||
renderer (ADR-0044) is the only relationship render path that ships.
|
||||
- `requirements.md` is unaffected (this is an ADR-tracked refinement of a
|
||||
decided area, like ADR-0044 itself); the change is cross-referenced
|
||||
from the commit + this ADR.
|
||||
|
||||
## Tests
|
||||
|
||||
- **Unit (`output_render.rs`):** the prose-asserting test
|
||||
`render_structure_with_relationships` (+ its snapshot) is removed; a
|
||||
new test asserts `render_structure` on a description **carrying** both
|
||||
inbound and outbound relationships emits the structure box but **no**
|
||||
`References:` / `Referenced by:` lines. The box/index/constraint tests
|
||||
are unaffected (their descriptions have no relationships).
|
||||
- **Integration (`walking_skeleton.rs`):** the misnamed
|
||||
`add_relationship_flow_shows_inbound_section_on_parent` (which sends an
|
||||
`AddColumn` and asserted the inbound prose) is inverted + renamed to
|
||||
assert the add-column confirmation shows the structure but **omits**
|
||||
the relationship prose.
|
||||
- **Unchanged:** the diagram tests (`show_list.rs` `show table`,
|
||||
`walking_skeleton.rs` `add relationship`) still pass — they already
|
||||
assert prose is absent and diagrams are present.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- The diagram form and its per-surface defaults (ADR-0044) — unchanged.
|
||||
- The OOS-7 user-configurable display setting (always-prose / -diagram /
|
||||
auto-by-width) — still a future follow-up; this ADR removes the prose
|
||||
*renderer*, not the *idea* of a prose mode.
|
||||
File diff suppressed because one or more lines are too long
+4
-2
@@ -2110,8 +2110,10 @@ impl App {
|
||||
// ADR-0044 §1 "relationship-relevant" reach: when a
|
||||
// relationship is the subject of the command (`show table`,
|
||||
// `add`/`drop relationship`), render the table's
|
||||
// relationships as compact diagrams; every other DDL echo
|
||||
// keeps the prose `References:` / `Referenced by:` form.
|
||||
// relationships as compact diagrams. Every other (incidental
|
||||
// DDL) echo renders structure only — no relationship block
|
||||
// at all (ADR-0050, issue #28; supersedes ADR-0044 §1's
|
||||
// prose retention for these surfaces).
|
||||
if matches!(
|
||||
command,
|
||||
Command::ShowTable { .. }
|
||||
|
||||
+34
-63
@@ -71,27 +71,22 @@ pub fn render_data_table(data: &DataResult) -> Vec<String> {
|
||||
render_table(&header_cells, &body, &alignments)
|
||||
}
|
||||
|
||||
/// Render a table-structure listing.
|
||||
/// Render an incidental-DDL structure echo (ADR-0050, issue #28).
|
||||
///
|
||||
/// Produces a header line (`<TableName>`), the schema table
|
||||
/// itself, and — for a structure that has FK relationships
|
||||
/// — `References:` / `Referenced by:` blocks below as plain
|
||||
/// indented text (relationship visualization is its own
|
||||
/// future ADR per §5 OOS-1).
|
||||
/// Display a relationship-endpoint column list (ADR-0043): the bare
|
||||
/// column for a single-column FK, `(a, b)` for a compound one.
|
||||
fn cols_disp(cols: &[String]) -> String {
|
||||
if cols.len() == 1 {
|
||||
cols[0].clone()
|
||||
} else {
|
||||
format!("({})", cols.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces a header line (`<TableName>`), the schema table, the
|
||||
/// `Indexes:` section, and the constraint section — **structure only**.
|
||||
/// Relationship information is deliberately omitted: a confirmation
|
||||
/// echo for a structural edit (`create table`, `add`/`drop`/`rename`/
|
||||
/// `change column`, `add`/`drop index`) reports the change just made,
|
||||
/// not the table's relationships, which the user did not touch. The
|
||||
/// relationship-subject surfaces (`show table`, `add`/`drop
|
||||
/// relationship`) render diagrams via [`render_structure_with_diagrams`]
|
||||
/// instead; relationships are one `show table <T>` away. ADR-0050
|
||||
/// supersedes ADR-0044 §1's "incidental DDL keeps prose" and the
|
||||
/// relationship-block half of ADR-0016 §5.
|
||||
#[must_use]
|
||||
pub fn render_structure(desc: &TableDescription) -> Vec<String> {
|
||||
let mut out = structure_box_lines(desc);
|
||||
out.extend(relationship_prose_lines(desc));
|
||||
out.extend(index_lines(desc));
|
||||
out.extend(constraint_lines(desc));
|
||||
out
|
||||
@@ -118,41 +113,6 @@ fn structure_box_lines(desc: &TableDescription) -> Vec<String> {
|
||||
out
|
||||
}
|
||||
|
||||
/// The `References:` / `Referenced by:` prose blocks (ADR-0016 §5),
|
||||
/// retained for the incidental DDL echoes (ADR-0044 §1).
|
||||
fn relationship_prose_lines(desc: &TableDescription) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
if !desc.outbound_relationships.is_empty() {
|
||||
out.push("References:".to_string());
|
||||
for r in &desc.outbound_relationships {
|
||||
out.push(format!(
|
||||
" {} → {}.{} ({}, on delete {}, on update {})",
|
||||
cols_disp(&r.local_columns),
|
||||
r.other_table,
|
||||
cols_disp(&r.other_columns),
|
||||
r.name,
|
||||
r.on_delete,
|
||||
r.on_update,
|
||||
));
|
||||
}
|
||||
}
|
||||
if !desc.inbound_relationships.is_empty() {
|
||||
out.push("Referenced by:".to_string());
|
||||
for r in &desc.inbound_relationships {
|
||||
out.push(format!(
|
||||
" {}.{} → {} ({}, on delete {}, on update {})",
|
||||
r.other_table,
|
||||
cols_disp(&r.other_columns),
|
||||
cols_disp(&r.local_columns),
|
||||
r.name,
|
||||
r.on_delete,
|
||||
r.on_update,
|
||||
));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Indexes section (ADR-0025), only when the table carries a
|
||||
/// user-created index. A UNIQUE index is marked `[unique]` (ADR-0035
|
||||
/// §4d).
|
||||
@@ -1591,11 +1551,23 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_structure_with_relationships() {
|
||||
fn render_structure_omits_relationship_prose() {
|
||||
// ADR-0050 (issue #28): the incidental-DDL structure echo never
|
||||
// carries the `References:` / `Referenced by:` block, even when
|
||||
// the description carries both inbound and outbound
|
||||
// relationships. (Relationship-subject surfaces render diagrams
|
||||
// via `render_structure_with_diagrams`, not this function.)
|
||||
let desc = TableDescription {
|
||||
name: "Customers".to_string(),
|
||||
columns: vec![col("id", Type::Serial, true, false)],
|
||||
outbound_relationships: Vec::new(),
|
||||
outbound_relationships: vec![RelationshipEnd {
|
||||
name: "cust_region".to_string(),
|
||||
other_table: "Regions".to_string(),
|
||||
other_columns: vec!["id".to_string()],
|
||||
local_columns: vec!["region_id".to_string()],
|
||||
on_delete: ReferentialAction::NoAction,
|
||||
on_update: ReferentialAction::NoAction,
|
||||
}],
|
||||
inbound_relationships: vec![RelationshipEnd {
|
||||
name: "cust_orders".to_string(),
|
||||
other_table: "Orders".to_string(),
|
||||
@@ -1609,15 +1581,14 @@ mod tests {
|
||||
check_constraints: Vec::new(),
|
||||
};
|
||||
let out = render_structure(&desc).join("\n");
|
||||
assert!(
|
||||
out.contains("Referenced by:"),
|
||||
"expected inbound relationship section:\n{out}",
|
||||
);
|
||||
assert!(
|
||||
out.contains("Orders.cust_id → id"),
|
||||
"expected inbound relationship line:\n{out}",
|
||||
);
|
||||
assert_snapshot!(out);
|
||||
// The structure box still renders.
|
||||
assert!(out.contains("Customers"), "structure header:\n{out}");
|
||||
assert!(out.contains("│ id"), "column row:\n{out}");
|
||||
// No relationship block in either direction.
|
||||
assert!(!out.contains("References:"), "no outbound prose:\n{out}");
|
||||
assert!(!out.contains("Referenced by:"), "no inbound prose:\n{out}");
|
||||
assert!(!out.contains("Orders.cust_id"), "no prose line:\n{out}");
|
||||
assert!(!out.contains("Regions"), "no prose line:\n{out}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
---
|
||||
source: src/output_render.rs
|
||||
expression: out
|
||||
---
|
||||
Customers
|
||||
┌──────┬────────┬─────────────┐
|
||||
│ Name │ Type │ Constraints │
|
||||
├──────┼────────┼─────────────┤
|
||||
│ id │ serial │ PK │
|
||||
└──────┴────────┴─────────────┘
|
||||
Referenced by:
|
||||
Orders.cust_id → id (cust_orders, on delete cascade, on update no action)
|
||||
@@ -494,7 +494,12 @@ fn add_relationship_flow_shows_parent_side_with_inbound_section() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_relationship_flow_shows_inbound_section_on_parent() {
|
||||
fn add_column_confirmation_omits_relationship_prose() {
|
||||
// ADR-0050 (issue #28): an incidental-DDL confirmation echo (here
|
||||
// `add column`) renders the structure only — never the
|
||||
// `References:` / `Referenced by:` relationship block — even when
|
||||
// the table carries relationships the user did not touch. The
|
||||
// relationships remain one `show table` away.
|
||||
let mut app = App::new();
|
||||
let customers = TableDescription {
|
||||
name: "Customers".to_string(),
|
||||
@@ -535,8 +540,17 @@ fn add_relationship_flow_shows_inbound_section_on_parent() {
|
||||
echo: None,
|
||||
});
|
||||
let rendered = rendered_text(&mut app, &Theme::dark(), 80, 24);
|
||||
assert!(rendered.contains("Referenced by:"), "{rendered}");
|
||||
assert!(rendered.contains("Orders.CustId → Id"), "{rendered}");
|
||||
// The structure box still renders (table name + the column box from
|
||||
// the returned description).
|
||||
assert!(rendered.contains("Customers"), "structure header:\n{rendered}");
|
||||
assert!(rendered.contains("Constraints"), "structure box:\n{rendered}");
|
||||
// The relationship block is gone — neither prose heading nor line.
|
||||
assert!(!rendered.contains("Referenced by:"), "no prose heading:\n{rendered}");
|
||||
assert!(!rendered.contains("References:"), "no prose heading:\n{rendered}");
|
||||
assert!(
|
||||
!rendered.contains("Orders.CustId → Id"),
|
||||
"no prose line:\n{rendered}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user