Skip to content

feat(trail): update body/metadata by number, id, or branch (incl. branchless)#1493

Open
paxos wants to merge 5 commits into
mainfrom
paxos/cli-trail-body-write
Open

feat(trail): update body/metadata by number, id, or branch (incl. branchless)#1493
paxos wants to merge 5 commits into
mainfrom
paxos/cli-trail-body-write

Conversation

@paxos

@paxos paxos commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

https://entire.io/gh/entireio/cli/trails/637

TL;DR — Makes entire trail update address any trail by number, id, or branch — including branchless trails — so their body and metadata (status/title/labels) become editable from the CLI, plus file/stdin body input. CLI-only, no API changes. Stacked on #1476 (the body-read PR).

Problem

entire trail update was NoArgs + --branch-only, so a branchless trail (no branch) could not be targeted at all — neither its body nor its metadata was editable from the CLI. show/checkout/delete already resolve trails by number/id/branch; update did not.

What changed (CLI only — no API changes)

  • Targeting: update takes a positional [<trail>] and a --trail selector (number, id, or branch); --branch kept for back-compat. This unblocks branchless trails for every field — not just the body but also --status, --title, --add-label/--remove-label (e.g. entire trail update 1488 --status open).
  • Body input: --body-file <path> and stdin via --body - / --body-file -, for multi-line / image-rich markdown.
  • PATCH split: body and metadata are sent as separate PATCHes (the API rejects combining them — the body is a collaborative editor document, metadata is a row update, no shared transaction). Metadata first; a body failure after a metadata success is reported as such.
  • Interactive form seeds the body from the real body_document instead of the empty metadata column, so it no longer clobbers the body.

Scope (relative to the two tracking plans)

This PR is the update half of the work:

  • ✅ Body write via update (+ file/stdin) — cli-trail-body-rw Step 2
  • update selector / branchless targeting (body and metadata) — cli-trail-targeting-consistency Step 1
  • ➖ Body read in show — provided by the base PR feat(trail): show the description and a browser URL in trail show #1476, not re-implemented here
  • Out of scope: delete by id/branch (cli-trail-targeting-consistency Step 2) — delete stays number-only in this PR

Test plan

  • go test ./cmd/entire/cli/ — full package green.
  • New unit tests: body-input resolution (literal / file / stdin / mutual exclusion), the metadata-vs-body PATCH split, branchless-number write, numberless-trail rejection, selector arg acceptance.
  • Built binary verified: entire trail update 1488 --body-file ./b.md, --body -, --trail, by id/branch, and --status on a branchless trail.

Notes

`entire trail update` now accepts a positional <trail> and a --trail selector
(number, id, or branch), so branchless trails are addressable; --branch is kept
for back-compat. Body input gains --body-file and stdin (--body - / --body-file -).

Body and metadata are sent as separate PATCHes since the API rejects combining
them, and the interactive form seeds the body from the real editor document
instead of the empty metadata column.
Copilot AI review requested due to automatic review settings June 22, 2026 18:34

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2c7bcaa. Configure here.

Comment thread cmd/entire/cli/trail_cmd.go Outdated
inputs.StatusChanged = true
inputs.Title = title
inputs.TitleChanged = true
inputs.Body = body

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Failed load still wipes body

Medium Severity

If loading the current body for the interactive form fails, the form still starts with an empty body, and a successful submit always marks the body as changed. A separate body PATCH then sends an empty string, which can erase the existing collaborative document when the user only meant to update title or status.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2c7bcaa. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3940a99. The interactive form now fails if the current body cannot be loaded instead of seeding an empty body, so an edit can no longer overwrite a body that merely failed to fetch. On top of that (eed63eb), a field is only marked changed when the user actually edits it, so an unedited body is never re-sent.

— response written by Claude (AI), on behalf of @paxos

Comment thread cmd/entire/cli/trail_cmd.go Outdated
inputs.StatusChanged = true
inputs.Title = title
inputs.TitleChanged = true
inputs.Body = body

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Trimmed seed rewrites body whitespace

Low Severity

Interactive updates seed the body from fetchTrailDescription, which trims leading and trailing whitespace from the snapshot, but the command still always PATCHes the body after the form and documents that CLI body input is written verbatim. Submitting without editing the body field persists the trimmed text and can drop meaningful surrounding whitespace from the live document.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2c7bcaa. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3940a99. The interactive form now seeds from the raw text_snapshot via a new fetchTrailBody (no TrimSpace), so an edited save preserves the document’s whitespace. And an unedited body is not PATCHed at all (eed63eb), so a no-op save is non-destructive. fetchTrailDescription stays trimmed for show display, delegating to fetchTrailBody.

— response written by Claude (AI), on behalf of @paxos

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances entire trail update to support updating both metadata and the collaborative body for any trail (including branchless trails) by accepting a selector via positional arg / --trail (number, id, or branch) while keeping --branch for backward compatibility. It also adds body input via --body-file and stdin (-), and implements the required API behavior of sending metadata and body as separate PATCH requests.

Changes:

  • Extend trail update to accept a trail selector (arg / --trail / legacy --branch) and resolve the trail via the same selector logic used by other trail commands.
  • Add body input resolution for --body, --body-file <path>, and stdin (--body - / --body-file -).
  • Split updates into two PATCH calls (metadata first, then body) and add unit tests covering body input resolution + PATCH splitting and numberless-trail rejection.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
cmd/entire/cli/trail_cmd.go Updates trail update CLI surface/selector handling, adds body input resolution + stdin/file support, and implements split PATCH behavior.
cmd/entire/cli/trail_cmd_test.go Adjusts positional-arg test coverage and adds new unit tests for body-input resolution and split PATCH semantics.

Comment thread cmd/entire/cli/trail_cmd.go Outdated
Comment on lines +952 to +959
body = ""
if found.Number > 0 {
if bt, derr := fetchTrailDescription(ctx, client, forge, owner, repoName, found.Number); derr == nil {
body = bt
} else {
fmt.Fprintf(errW, "Warning: could not load current body: %v\n", derr)
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Adopted both suggestions in 3940a99: (a) the interactive update now fails when the current body can’t be loaded rather than seeding empty, and (b) it seeds from the raw text_snapshot (no TrimSpace) via fetchTrailBody. Combined with only-send-edited-fields (eed63eb), a no-op save is non-destructive and an edit preserves whitespace. New tests: TestFetchTrailBodyReturnsRawUntrimmedSnapshot, TestFetchTrailBodyReportsMissingDocument.

— response written by Claude (AI), on behalf of @paxos

The interactive update form always marked the body changed and PATCHed it. If
the current body failed to load (empty seed) or was whitespace-trimmed by the
detail endpoint, an unedited submit would erase or rewrite the live editor
document. Mark each field changed only when the user edits it away from its
seed, and skip the request entirely when nothing changed.
@paxos

paxos commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both Bugbot findings in eed63eb:

Both stemmed from the interactive (no-flags) form always marking the body changed and PATCHing it. Now each field is marked changed only when the user edits it away from its seed (interactiveUpdateInputs), so:

  • Failed body load no longer wipes — if the current body fails to load (empty seed) and the user only edits status/title, the body is not re-sent.
  • Trimmed seed no longer rewrites whitespace — an unedited body (seeded from the trimmed snapshot) is not PATCHed.

Also added a "No changes" short-circuit when nothing was edited. New unit tests: TestInteractiveUpdateInputsMarksOnlyEditedFields, TestInteractiveUpdateInputsUneditedBodyIsNotResent. Full package tests green.

This comment was generated by AI (Claude).

Per review: the interactive update now seeds the body from the raw text_snapshot
(via fetchTrailBody) so an edited save preserves the document whitespace, and
fails when the current body cannot be loaded instead of seeding empty — which
would let an edit silently overwrite a body that merely failed to fetch.
fetchTrailDescription is kept (trimmed) for `show` display.
@paxos

paxos commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the Copilot review in 3940a99:

  • Fail on load error — the interactive update now returns an error if the current body cannot be loaded, instead of seeding an empty body (which would let an edit silently overwrite a body that merely failed to fetch).
  • Raw seed (no TrimSpace) — the form is seeded from the raw text_snapshot via a new fetchTrailBody, so an edited save preserves the document’s original whitespace. fetchTrailDescription is kept (trimmed) for show display, now delegating to fetchTrailBody.

Combined with the earlier change (only re-send a field the user actually edited), an interactive no-op is non-destructive and an edit preserves whitespace. New tests: TestFetchTrailBodyReturnsRawUntrimmedSnapshot, TestFetchTrailBodyReportsMissingDocument. Full package tests green.

This comment was generated by AI (Claude).

paxos added 2 commits June 22, 2026 11:57
Avoid adding new "hello world" string literals (goconst occurrence count) by
using local constants, and rename unused httptest handler params to _.
Per Codex review: feeding --branch into the generic number/id/branch selector
made `--branch 123` resolve trail #123 instead of the branch named "123".
Resolve --branch via findTrailByBranch (branch-only) and keep the positional
arg / --trail on the generic selector.
@paxos paxos marked this pull request as ready for review June 22, 2026 20:18
@paxos paxos requested a review from a team as a code owner June 22, 2026 20:18
Base automatically changed from feat/trail-show-intent-and-url to main June 22, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants