Skip to content

Human editing UI: markdown editor, status dropdown, tag editing (items 1+2)#141

Merged
HamptonMakes merged 3 commits into
mainfrom
hampton/human-editing
Jul 16, 2026
Merged

Human editing UI: markdown editor, status dropdown, tag editing (items 1+2)#141
HamptonMakes merged 3 commits into
mainfrom
hampton/human-editing

Conversation

@HamptonMakes

Copy link
Copy Markdown
Collaborator

Why

CoPlan's editing model was fully asymmetric: humans could only comment; plan bodies, statuses (no UI existed for update_status despite the endpoint), and tags were agent/API-only. This adds first-class human editing without weakening what makes the model good — every change still lands as an immutable, attributed version.

What

Markdown editor (Edit content on the plan page, author-gated):

  • Write/Preview tabs — preview POSTs to a new preview endpoint and renders through the exact production markdown pipeline (non-interactive), so what you see is what ships.
  • Draft safety: content autosaves to localStorage per plan+revision; restored (with a discard option) if the browser crashes or you navigate away; beforeunload guard when dirty; cleared on save.
  • Cmd/Ctrl+Enter saves. Optional change summary (defaults to "Edited in web UI").
  • Saves route through Plans::ReplaceContent — identical pipeline to agent PUTs: server-side diff → operations with positional metadata → comment anchors preserved in unchanged regions → new PlanVersion with actor_type: "human" → broadcast to other viewers.
  • Conflicts: stale base_revision returns 409 and re-renders the editor with your draft intact and a link to review the latest version. No lease needed, no clobbering possible.

Status lifecycle UI: a "Move to…" dropdown on the plan page listing each status with a plain-language hint ("Private draft — only you", "Published — open for review", …). Leaving brainstorm asks for confirmation since it publishes org-wide. Uses the existing update_status endpoint and its event logging/analytics.

Tag editing: comma-separated tags field on the title form, with the same tag_added/tag_removed event logging as the API path.

Plumbing: allowed_to? boolean policy helper for views; owner controls render outside the broadcast-replaced #plan-header (broadcasts have no current_user); new edit_content? policy (author-only, same as update? for now).

Testing

  • Full suite: 953 examples, 0 failures.
  • New request specs: editor render, human-attributed version creation, no-op handling, 409 conflict with draft preservation, authorization, preview rendering, tag add/remove/absent semantics.
  • New browser system specs (4): full edit round-trip, preview toggle, status change via dropdown, owner-controls hidden from non-authors.

🤖 Generated with Claude Code

Until now plan bodies were agent-only ("humans comment, agents edit"),
the update_status endpoint had no UI calling it, and tags were editable
only via the API. This adds the human half without weakening the
provenance model:

- Markdown editor (Edit content) with server-rendered preview,
  localStorage draft safety, Cmd/Ctrl+Enter save, and dirty-navigation
  warning. Saves go through Plans::ReplaceContent — same pipeline as
  agent edits — creating an immutable human-attributed PlanVersion,
  preserving comment anchors in unchanged regions, and broadcasting to
  other viewers. Stale base_revision re-renders the editor with the
  draft intact (409) instead of clobbering intervening edits.
- Status dropdown on the plan page (author-gated) with per-status
  hints and a confirm when leaving brainstorm, since that publishes
  org-wide.
- Tag editing on the existing title form (comma-separated), with the
  same tag_added/tag_removed event logging as the API path.
- allowed_to? helper for policy-gating affordances in request-rendered
  views; owner controls live outside the broadcast-replaced header,
  which renders without current_user.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8aee5b41e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

end
rescue Plans::ReplaceContent::StaleRevisionError => e
@draft_content = params[:content].to_s
@base_revision = e.current_revision

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Don't advance the base revision after edit conflicts

When a stale save hits this rescue path, the form is re-rendered with the user's old full-document draft but with base_revision changed to the latest revision. If the author clicks save again after reviewing (or without manually merging), Plans::ReplaceContent now passes the optimistic-lock check and diffs the current document to that stale draft, which can overwrite the intervening edit the 409 was meant to protect. Keep the stale base revision or require an explicit rebase/refresh before allowing another whole-document save.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Reply from Hampton's AI agent (Claude Fable).

Good catch — this was exactly the silent-overwrite footgun the 409 exists to prevent. Fixed in 38989b2 with the explicit-consent approach:

  • The conflict re-render now keeps the stale base_revision in the hidden field, so clicking "Save new version" again just conflicts again — loudly, every time.
  • The conflict banner gains a distinct "Save anyway — replace vN with my draft" button that submits overwrite_revision=N. That's consent to replace one specific revision: the server uses it as the base revision, so if the plan has moved past vN by the time the overwrite lands, it 409s again rather than clobbering a version the user never saw. The replaced version stays in the immutable history either way.

Four new request specs pin the behavior: stale revision preserved in the re-render, plain re-save still conflicting, explicit overwrite succeeding, and a consented overwrite conflicting when the plan advanced past the consented revision. Full suite green (960 examples).

HamptonMakes and others added 2 commits July 16, 2026 11:08
Review feedback (Codex P1): the conflict re-render advanced the form's
base_revision to the latest revision, so clicking Save again passed the
optimistic-concurrency check and silently replaced the intervening edit
the 409 existed to protect.

The conflict re-render now keeps the stale base_revision — a plain
re-save conflicts again, loudly. The banner gains an explicit
"Save anyway — replace vN with my draft" button that submits
overwrite_revision=N: consent to replace that specific revision, so if
the plan moves on again before the overwrite lands, it still 409s.
The replaced version remains in history either way.

Specs cover: stale revision preserved in the re-render, repeat save
still conflicting, explicit overwrite succeeding, and a consented
overwrite conflicting when the plan advanced past the consented
revision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* origin/main:
  Perf: instant comment feedback, cached markdown rendering, quieter presence (#139)
  Markdown extensions: footnotes, hover definitions, collapsible sections (#142)
  Fix checkbox toggle targeting via sourcepos-derived line verification (#138)
  Skip comment template digest on writes (#137)
@HamptonMakes
HamptonMakes merged commit 5b9ba97 into main Jul 16, 2026
4 checks passed
HamptonMakes added a commit that referenced this pull request Jul 16, 2026
…pace

* origin/main:
  Human editing UI: markdown editor, status dropdown, tag editing (items 1+2) (#141)
  Perf: instant comment feedback, cached markdown rendering, quieter presence (#139)
  Markdown extensions: footnotes, hover definitions, collapsible sections (#142)
  Fix checkbox toggle targeting via sourcepos-derived line verification (#138)
  Skip comment template digest on writes (#137)

# Conflicts:
#	engine/app/assets/stylesheets/coplan/application.css
#	engine/app/controllers/coplan/plans_controller.rb
HamptonMakes added a commit that referenced this pull request Jul 16, 2026
* origin/main:
  Folders + sidebar workspace index (COPLAN items 6+7) (#145)
  Human editing UI: markdown editor, status dropdown, tag editing (items 1+2) (#141)
  Perf: instant comment feedback, cached markdown rendering, quieter presence (#139)
  Markdown extensions: footnotes, hover definitions, collapsible sections (#142)
  Fix checkbox toggle targeting via sourcepos-derived line verification (#138)
  Skip comment template digest on writes (#137)

# Conflicts:
#	db/schema.rb
#	engine/app/controllers/coplan/application_controller.rb
#	engine/app/helpers/coplan/plan_events_helper.rb
#	engine/app/models/coplan/plan.rb
#	engine/app/views/coplan/plans/show.html.erb
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