Skip to content

feat(output): render branch merge/sync conflicts as a table on a terminal (#169) - #205

Merged
thomaschristory merged 2 commits into
mainfrom
feat/conflict-table
Oct 24, 2026
Merged

thomaschristory merged 2 commits into
mainfrom
feat/conflict-table

Conversation

@thomaschristory

Copy link
Copy Markdown
Owner

Fixes #169.

On a terminal, a conflict envelope (exit 10) printed details.conflicts as a raw Python dict. The panel cropped it, along with the end of the message.

Change (nsc/output/errors.py, rich stderr only):

  • details.conflicts renders as a table with one row per conflicting field: object, field, original, branch, main. The object name appears on its first row.
  • The table follows the shape that netbox-branching 1.2.2 returns: object_type, object_repr, conflicts, and conflicting_data.{original,branch,main}.
  • Non-string values render as JSON. A missing conflicting_data still lists the fields.
  • An empty list or any non-dict entry keeps the raw details: line, so nothing is dropped.
  • Other details, such as branch, stay on the details: line.
  • render_to_json is untouched.

The error panel's console no longer uses soft_wrap=True. Inside a Panel, that cropped long lines instead of wrapping them, which cut off re-run with --acknowledge-conflicts.

Live, NetBox 4.7.3 + Branching 1.2.2, TTY:

╭──────────────────────────── nsc error ─────────────────────────────╮
│ conflict: branch 'cfl169b' has 1 unacknowledged conflict(s); review │
│ them and re-run with --acknowledge-conflicts                        │
│ details:  {'branch': 'cfl169b'}                                     │
│ ╭────────────────────┬─────────────┬──────────┬─────────────┬───────────╮
│ │ object             │ field       │ original │ branch      │ main      │
│ ├────────────────────┼─────────────┼──────────┼─────────────┼───────────┤
│ │ extras.tag cfl169b │ description │          │ from-branch │ from-main │
│ ╰────────────────────┴─────────────┴──────────┴─────────────┴───────────╯

-o json still returns the raw list. The branching e2e suite passed locally (4 passed).

Tests: tests/output/test_errors_conflicts.py uses the real payload shape. It covers:

  • per-field rows;
  • the details line keeping its other keys;
  • a missing conflicting_data;
  • JSON-rendered non-string values;
  • unreadable payloads falling back to the details line;
  • JSON output unchanged;
  • a long message wrapping, which fails on main.

Docs: docs/guides/branching.md (conflicts section), CHANGELOG.

https://claude.ai/code/session_01RD8NgCVtp3yYFd6NSZpbUb

…inal (#169)

A `conflict` envelope printed details.conflicts as a raw, truncated
Python dict. On rich stderr, render one row per conflicting field with
the object and its original/branch/main values (shape from
netbox-branching 1.2.2). Unexpected payloads keep the raw details line;
JSON output is unchanged.

Also drop soft_wrap from the error panel console: inside a Panel it
cropped long lines, hiding e.g. "re-run with --acknowledge-conflicts".

Claude-Session: https://claude.ai/code/session_01RD8NgCVtp3yYFd6NSZpbUb
Rich's markup escape() and Text() don't strip ESC, so server-sourced
text in the TTY error panel (message, endpoint, op id, and the new
conflict table cells) could carry terminal escape sequences, e.g. an
object name that retitles or clears the terminal. Show control and
format characters as visible \x.. escapes. Flagged by the automated
security review on #205.

Claude-Session: https://claude.ai/code/session_01RD8NgCVtp3yYFd6NSZpbUb
@thomaschristory

Copy link
Copy Markdown
Owner Author

Pushed 654e777: security review flagged raw ESC sequences in the error panel (conflict cells, and pre-existing for error/endpoint/op lines). Control/format chars now render as visible \x.. escapes; test added.

@thomaschristory
thomaschristory merged commit 3238fcd into main Oct 24, 2026
15 checks passed
@thomaschristory
thomaschristory deleted the feat/conflict-table branch October 24, 2026 21:58
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.

Branching: render merge/sync conflicts as a readable table on a TTY

1 participant