Skip to content

feat(storage): show column descriptions in table-detail human output - #642

Merged
padak merged 2 commits into
mainfrom
feat/table-detail-column-descriptions
Aug 22, 2026
Merged

feat(storage): show column descriptions in table-detail human output#642
padak merged 2 commits into
mainfrom
feat/table-detail-column-descriptions

Conversation

@padak

@padak padak commented Aug 22, 2026

Copy link
Copy Markdown
Member

Why

0.88.0 (#624) was specifically about making column descriptions visible everywhere: the write moved to the native PUT .../tables/{id}/definition endpoint so the text reaches the Keboola UI, the MCP server's get_tables, and the Snowflake COMMENT / BigQuery column description. --json carried it too, on column_details[].description.

kbagent's own human-mode Columns table was the one surface still showing nothing. So this sequence — the obvious way to check your own work — was the one way that did not work:

kbagent storage describe-column --project P --table-id T --column amount_usd="Gross order value in USD"
kbagent storage table-detail   --project P --table-id T     # ...no descriptions anywhere

The column has never existed: git log -S "Description" -- src/keboola_agent_cli/commands/_storage_table_detail.py is empty.

What changed

A Description column on that Rich table, populated from col.get("description").

Before / after (100-column terminal, all three description tiers):

┏━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Name       ┃ Type      ┃ Nullable ┃ Description                                                  ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ order_id   │ INTEGER   │          │                                                              │
│ created_at │ TIMESTAMP │          │ Order creation time (UTC).                                   │
│ amount_usd │ NUMERIC   │ yes      │ Gross order value in USD, excluding tax and shipping;        │
│            │           │          │ refunds appear as separate negative rows.                    │
│ notes      │           │          │ Free-text note [see runbook]                                 │
└────────────┴───────────┴──────────┴──────────────────────────────────────────────────────────────┘

Three decisions worth stating, since the issue left them open:

Hidden when empty. The column is added only when at least one column actually has a description — the same "no data, no chrome" rule render_table_layout already follows for the partitioning lines. An undocumented table's output is byte-identical to before. This is a real emptiness test, not a test for "": services/_table_detail._column_details sets description only when one of the three tiers resolved.

Wrapped and capped, never truncated. Verifying what you just wrote is the whole point, so an ellipsis would defeat it. overflow="fold" breaks even a long unbroken token instead of hiding its tail, and max_width=60 stops a wide terminal from stretching the cell across the screen while Rich shrinks it further on a narrow one. Both directions are asserted.

Escaped. Descriptions are user-authored free text and Rich renders str cells as markup — [note] would vanish and an unbalanced tag can raise. escape() matches what the display_name line above already does.

--json is untouched. All three tiers the service already resolves (native definitioncolumnMetadata KBC.description → legacy flat KBC.column.*) render, and the legacy-key migration warning still prints below the table.

Tests

Five tests in TestColumnDescriptions, alongside the existing human-render tests for that module. Each was verified to fail against the matching mutation of the production code — drop max_width → the sprawl test dies; drop escape() → the markup test dies; force show_description = True → the empty-column test dies.

One trap worth flagging for future readers, and called out in the test docstring: tests/conftest.py has an autouse fixture pinning COLUMNS=200 for every test (added after a host-dependent width silently cut a value in half on Windows CI). A "fits in 80 columns" assertion that does not override it asserts nothing at all — the first draft of that test passed for the wrong reason.

Version — no bump

Deliberately not bumped: v0.88.0 shipped earlier today and the next number is the release step's call, so this PR stays on 0.88.0 and gets renumbered at release time.

Two consequences worth knowing about, both handled:

  • No changelog key. A 0.88.1 entry with no matching release fails make changelog-check, so the entry was removed. Paste-ready text for whoever cuts the release:

    UX: kbagent storage table-detail now shows column descriptions in its human-mode Columns table. 0.88.0 (storage describe-column writes column descriptions where the MCP server never reads them #624) made a description visible in the Keboola UI, in the MCP server's get_tables and in the warehouse's own COMMENT, and --json has carried it on column_details[].description all along -- kbagent's own terminal output was the one surface still showing nothing, so storage describe-column followed by storage table-detail (the obvious way to check your own work) was the one way that did not work. The column appears only when at least one column actually has a description, the same rule the partitioning block follows, so an undocumented table's output is byte-identical to before. Long text wraps rather than truncating -- verifying what you just wrote is the point -- and the cell is width-capped so a wide terminal does not stretch it across the screen. --json is unchanged.

  • No (since vX.Y.Z) markers. make version-gate-check rejects a gate naming a version with no changelog entry — an agent reading a gate for a version that never ships refuses a command the user actually has. The three doc notes (CLAUDE.md, references/commands-reference.md, references/gotchas.md) therefore anchor on 0.88.0, a real release, and state what that version does not do: "On 0.88.0 it did NOT — there the descriptions were readable only through --json, so a blank-looking terminal table on that version does not mean the write failed." True whatever number this ships under. gotchas.md carries an inline release-step reminder to tag it once the version is known.

make check is green end to end: lint, format, SKILL.md, version sync, version gates, command sync, changelog completeness, error codes, sentinel guards, LOC budget, 5862 tests.

0.88.0 (#624) moved the column-description write to the native
`PUT .../tables/{id}/definition` endpoint precisely so the text would show
up everywhere: the Keboola UI, the MCP server's `get_tables`, and the
Snowflake COMMENT / BigQuery column description. `--json` carried it too, on
`column_details[].description`.

kbagent's own human-mode Columns table was the one surface still showing
nothing -- so `storage describe-column` followed by `storage table-detail`,
the obvious way to check your own work, was the one way that did not work.

Add a `Description` column to that Rich table:

* Shown only when at least one column actually has a description, matching
  `render_table_layout`'s existing "no data, no chrome" rule -- an
  undocumented table's output is byte-identical to before.
* Wrapped and width-capped, never truncated. Verifying what you just wrote
  is the point, so an ellipsis would defeat it; `overflow="fold"` breaks even
  a long unbroken token rather than hiding its tail, and `max_width=60` stops
  a wide terminal from stretching the cell across the screen.
* Escaped. Descriptions are user-authored free text, and Rich would otherwise
  read `[note]` as markup (or raise on an unbalanced tag).

`--json` is untouched. All three description tiers the service already
resolves (native definition, `columnMetadata` KBC.description, legacy flat
key) render, and the legacy-key migration warning still prints below.

Tests cover the render, the empty-column guard, markup escaping, wrapping at
80 columns, and the width cap at 200; each was verified to fail against the
matching mutation of the production code. Note that the 80-column case has to
override conftest's autouse `COLUMNS=200` fixture explicitly -- without that
it silently asserts nothing.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

v0.88.0 shipped earlier today, so this change belongs to whatever version
comes next -- but that number is the release step's to choose, not this PR's.
Reverts pyproject/plugin.json/marketplace.json/uv.lock to 0.88.0 and removes
the 0.88.1 changelog key (an entry with no matching release fails
`make changelog-check`).

The doc surfaces no longer name 0.88.1 either. `make version-gate-check`
rejects any `(since vX.Y.Z)` marker that is not a CHANGELOG key -- an agent
reading a gate for a version that never ships refuses a command the user
actually has. The three notes now anchor on 0.88.0, a real release, and say
what that version does NOT do:

  "Human mode's Columns table shows a Description column. On 0.88.0 it did
   NOT -- there the descriptions were readable only through --json, so a
   blank-looking terminal table on that version does not mean the write
   failed."

That is true regardless of the number this eventually ships under. gotchas.md
carries an explicit release-step reminder to tag it once known.
@padak
padak merged commit f2f91cd into main Aug 22, 2026
4 checks passed
@padak
padak deleted the feat/table-detail-column-descriptions branch August 22, 2026 20:34
padak added a commit that referenced this pull request Aug 22, 2026
…has.md

Both '(Release step: once this ships, tag this sentence (since vX.Y.Z)...)'
parentheticals in the storage-descriptions gotcha referred to behavior that
has since merged: the table-detail human-mode Description column (#642) and
the describe-batch --from-file shape rejection (#645). Neither commit is
contained in the v0.88.0 tag and main is bumped to 0.89.0, so both are
tagged (since v0.89.0) -- a version version-gate-check resolves via its
existing changelog entry. This is a live instance of the leftover-placeholder
failure mode this PR's release checklist step 4 now guards against.
padak added a commit that referenced this pull request Aug 22, 2026
pyproject/plugin.json/marketplace.json were already renumbered to 0.89.0 by
v0.88.0.

Changelog: adds 0.89.0 entries for #645 (describe-batch --from-file shape
validation, issue #640), #642 (table-detail human column descriptions), #620
(sync-action forwards root authorization/runtime), #517 (stable metavar
contract, issue #513), #586 (documented prompt budget gated against the
enforced one, issue #585) and #641 (docs-only), and decorates the existing

Silent-drift surfaces:

* gotchas.md -- resolves both "(Release step: ... tag this sentence)"
  placeholders. Both were left by commits AFTER the v0.88.0 tag (#642 and
  #645), so both are tagged (since v0.89.0), not 0.88.0. Adds the #620 gotcha:
  below 0.89.0 a sync action on an OAuth / Service-Account component died with
  an opaque empty-body 400 because the broker reference was never forwarded.
* #620 shipped with no doc surfaces at all -- CLAUDE.md, AGENT_CONTEXT and
  commands-reference.md now carry the forwarding rule (root only, never
  row-overridden, only when non-empty) with its version gate.
* #645 never reached CLAUDE.md -- the describe-batch shape check and its
  behaviour change are recorded there now; commands-reference gains the
  version tag.
* #642's human Description column is version-tagged in CLAUDE.md,
  commands-reference.md, AGENT_CONTEXT and storage-describe-workflow.md.
* #643 was otherwise complete; adds the two surfaces it did not touch --
  safe-write-workflow.md (delete is reversible; never blind-retry on <= 0.88.x)
  and a keboola-expert.md matrix row for delete/restore/trash-list.
  keboola-expert.md is 49 774 B, well inside the 70 000 B budget.

make check green: 5934 passed, 12 skipped. version-gate-check resolves all 438
markers across 72 versions.
padak added a commit that referenced this pull request Aug 23, 2026
#648)

* docs: stop bumping the version in feature PRs; releases become dedicated release PRs

Parallel feature PRs (one AI session per issue) each bumping pyproject.toml
collide on every merge and silently renumber releases -- the KNOWN_UNRELEASED
list in scripts/generate_changelog.py is the accumulated damage of that
pattern. Version bumps and changelog.py entries now happen ONLY in a
dedicated release PR that batches everything merged since the last release.

- CLAUDE.md Versioning: new 'Version bumps happen ONLY in a dedicated
  release PR' subsection with the two-step flow and the vNEXT placeholder
  convention for version-gated docs in feature PRs (version-gate-check
  rejects guessed numeric versions in per-PR CI, so the placeholder is the
  only workable tag before the release assigns a number).
- CONTRIBUTING.md: new 'No version bumps in feature PRs' commit convention;
  per-command gotchas checklist and sync map updated for vNEXT; 'Releasing
  a new version' rewritten around the release PR -- new step 1 (collect
  merged PRs since the last tag as the exact scope of the changelog and
  release notes) and step 4 (replace every vNEXT, grep must come back
  empty); beta releases documented as the one deliberate exception where
  the bump rides the feature branch.

* docs(plugin): resolve the two stale release-step placeholders in gotchas.md

Both '(Release step: once this ships, tag this sentence (since vX.Y.Z)...)'
parentheticals in the storage-descriptions gotcha referred to behavior that
has since merged: the table-detail human-mode Description column (#642) and
the describe-batch --from-file shape rejection (#645). Neither commit is
contained in the v0.88.0 tag and main is bumped to 0.89.0, so both are
tagged (since v0.89.0) -- a version version-gate-check resolves via its
existing changelog entry. This is a live instance of the leftover-placeholder
failure mode this PR's release checklist step 4 now guards against.
padak added a commit that referenced this pull request Aug 23, 2026
pyproject/plugin.json/marketplace.json were already renumbered to 0.89.0 by
v0.88.0.

Changelog: adds 0.89.0 entries for #645 (describe-batch --from-file shape
validation, issue #640), #642 (table-detail human column descriptions), #620
(sync-action forwards root authorization/runtime), #517 (stable metavar
contract, issue #513), #586 (documented prompt budget gated against the
enforced one, issue #585) and #641 (docs-only), and decorates the existing

Silent-drift surfaces:

* gotchas.md -- resolves both "(Release step: ... tag this sentence)"
  placeholders. Both were left by commits AFTER the v0.88.0 tag (#642 and
  #645), so both are tagged (since v0.89.0), not 0.88.0. Adds the #620 gotcha:
  below 0.89.0 a sync action on an OAuth / Service-Account component died with
  an opaque empty-body 400 because the broker reference was never forwarded.
* #620 shipped with no doc surfaces at all -- CLAUDE.md, AGENT_CONTEXT and
  commands-reference.md now carry the forwarding rule (root only, never
  row-overridden, only when non-empty) with its version gate.
* #645 never reached CLAUDE.md -- the describe-batch shape check and its
  behaviour change are recorded there now; commands-reference gains the
  version tag.
* #642's human Description column is version-tagged in CLAUDE.md,
  commands-reference.md, AGENT_CONTEXT and storage-describe-workflow.md.
* #643 was otherwise complete; adds the two surfaces it did not touch --
  safe-write-workflow.md (delete is reversible; never blind-retry on <= 0.88.x)
  and a keboola-expert.md matrix row for delete/restore/trash-list.
  keboola-expert.md is 49 774 B, well inside the 70 000 B budget.

make check green: 5934 passed, 12 skipped. version-gate-check resolves all 438
markers across 72 versions.
padak added a commit that referenced this pull request Aug 23, 2026
…es (#651)

Release prep for 0.89.0: adds the changelog entries for everything merged since v0.88.0 (#620, #642, #643, #644, #645, #646, #647, #648, #649, #650, #517, #586, #641), resolves every vNEXT placeholder left by feature PRs to v0.89.0 per the new #648 release process, closes the 10 gaps a full doc-surface audit found across the kbagent plugin (SKILL.md triggers, commands-reference, gotchas, workflow files, keboola-expert.md, AGENT_CONTEXT, CLAUDE.md), and records the live e2e verification evidence. Version files were already at 0.89.0 (bumped by #643); make version-sync is a no-op.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant