Skip to content

Add comments on tasks, with the person or agent that wrote each one - #545

Open
alex-clickhouse wants to merge 1 commit into
mainfrom
alex/task-comments
Open

alex-clickhouse wants to merge 1 commit into
mainfrom
alex/task-comments

Conversation

@alex-clickhouse

Copy link
Copy Markdown
Collaborator

Builds on #541 (merged), which added the plan section to the task detail.

What

People and the agent can add comments to a task. Each comment shows who wrote it:

  • A person's name, for a comment through the web UI or REST.
  • "Nerve for ", for a comment that the agent wrote in a session that the person started.

The task detail shows the comments below the task content, with a box to add one. A delete button shows on your own comments only. Board and list cards show the comment count.

The agent gets a new tool, task_comment. task_read shows the newest 50 comments.

Why

Tasks had no place for discussion. The only options were to edit the task body or to use task_update(note=...). Neither one records who wrote the text. On an instance with more than one person, the reader cannot tell who asked for what.

How

Storage. Migration v060 adds the table task_comments (id, task_id, body, actor_id, caused_by_actor_id, created_at) and the column tasks.comment_count. The two actor columns follow the existing attribution model (nerve/identity.py, registered in nerve/db/actor_columns.py):

  • A person who comments is both the actor and the cause.
  • The agent is the system actor, and the cause is the cause of its session.

Comments are not in the task's markdown file. A file cannot hold a trustworthy author, and an agent that rewrites the file would rewrite the comments too.

The task row. Adding or deleting a comment also updates comment_count, updated_at and revision, in the same transaction:

  • comment_count puts the count on every task row, so the board needs no extra query.
  • updated_at lets the existing task_updated broadcast refresh open task views. TaskComments reads the comments again when updated_at changes, the same way TaskPlan does. A comment by another person or by the agent shows without a reload.
  • revision changes because task_read shows comments. An agent that read the task before a new comment gets a conflict on an expect_revision write, and reads the task again.

Agent. task_comment(task_id, body) adds a comment. task_read puts the comments between two marker lines, before the revision line. task_write removes that block, so a read-edit-write round trip does not copy comments into the file. Each comment line is indented, so comment text cannot match a marker.

REST.

  • GET /api/tasks/{id}/comments
  • POST /api/tasks/{id}/comments: goes through the task_comment handler, like POST /api/tasks goes through task_create. Returns 404 for an unknown task and 422 for an empty body or a body longer than 10,000 characters.
  • DELETE /api/tasks/{id}/comments/{comment_id}: only the author can delete. Accounts have no roles, so no person can delete the words of another. Returns 403 for anybody else. Comments cannot be edited.

UI. New TaskComments component in TaskDetailBody, in preview mode, below the content. Both the full page and the board modal show it. It uses ActorAttribution for authors and MarkdownContent for bodies. Ctrl+Enter or Cmd+Enter sends. BoardCard and TaskCard show the count.

Not in this PR: editing, mentions, notifications, threads, comments in search, and waking the agent when a person comments. The agent sees a new comment the next time it reads the task.

Merge note

PR 3A of the user-test fixes (alex/fb-plan-links, not opened yet) also adds a migration, v059. This PR uses v060. Whichever of the two merges second must have its migration numbered above the other one (scripts/check_migration_numbers.py enforces it).

Testing

  • tests/test_task_comments.py (19 tests): migration rerun; counter, updated_at and revision on add and delete; rollback when the foreign key refuses an actor; REST attribution, 404, 422, author-only delete; agent attribution (system actor for the session's cause); task_read block, authors and the 50 limit; task_write round trip, including a comment that contains the end marker; a task file that contains marker lines and a fake comments block; a guarded write that a new comment refuses. test_tool_registry.py lists the new tool. The existing test_the_registry_lists_every_attribution_column covers the two new columns.
  • Mutation check: I removed each new guard and each new write in turn, and a test failed each time.
  • Backend: the full suite is left to CI. Locally the comment, task, attribution and tool registry tests pass. A partial local run of the full suite found one existing test that checked the last two entries of ACTOR_COLUMNS; it now checks membership.
  • Frontend: npm run build clean; npm test 1005 passed (the base has 998; 7 new in TaskComments.test.tsx). eslint finds no new problems.
  • Browser check (Playwright, dark and light, 1280x800 and 375x812) with the comment endpoints stubbed: the authors show as "Bob", "Nerve for " and ""; delete shows only on the viewer's comment; adding and deleting work; the board and list cards show the count; the modal shows the section; axe finds no contrast problems in the section; no horizontal overflow.

🤖 Generated with Claude Code

People and the agent can now add comments to a task. Each comment
records who wrote it and on whose behalf, like the other attributed
writes: a person through the UI or REST is both, and the agent is the
system actor writing for the cause of its session. The UI shows
"Nerve for <person>" for an agent comment.

Storage: migration v060 adds the table task_comments and the column
tasks.comment_count. Comments are not in the task's markdown file,
because a file cannot hold a trustworthy author and an agent that
rewrites the file would rewrite the comments too. Adding or deleting a
comment also updates comment_count, updated_at and revision in the same
transaction. Thus the count is on every task row, the task_updated
broadcast refreshes open task views, and an agent that read the task
before the comment gets a revision conflict on a guarded write.

Agent: the new tool task_comment adds a comment. task_read shows the
newest 50 comments in a marked block before the revision line, and
task_write strips that block, so a read-edit-write round trip does not
copy comments into the file. task_read indents each file line that
starts with "<!-- nerve:", so only Nerve's own lines start with a
marker: file content cannot pose as comments or be removed by the strip.

REST: GET, POST and DELETE on /api/tasks/{id}/comments. POST goes
through the task_comment handler. Only the author of a comment can
delete it, because accounts have no roles.

UI: the task detail shows the comments below the content in preview
mode, with a box to add one and a delete button on your own comments.
Board and list cards show the comment count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alex-clickhouse
alex-clickhouse marked this pull request as ready for review October 21, 2026 18:49
@nerveai

nerveai commented Oct 21, 2026

Copy link
Copy Markdown

Nerve PR review · ✅ Ready to merge

This PR adds attributed comments on tasks. Comments go in a task_comments table with the usual actor/cause pair. A task_comment tool goes in the shared registry, and the REST POST goes through it too. The task view gets a comments section. Keeping comments out of the markdown file is the right call. The task_read block with its strip on task_write, plus the indented file markers, closes both the round-trip hole and the spoofing hole. A comment bumps revision, which matches v052: a fenced writer has to re-read first. The feature fills a real gap on instances with more than one person.

Completeness: 19 backend and 7 frontend tests; docs (tasks.md, accounts.md, both TOOLS.md templates); ACTOR_COLUMNS registered; upsert_task's ON CONFLICT leaves comment_count alone.

Worth considering:

Checked: 866 task/DB/attribution/registry tests in an offline sandbox at ff99d08, all passed; CI green (full backend suite, frontend build); read the migration, TaskStore, handler, routes, TaskComments; frontend not run locally · automated review by nerveai — mention @nerveai to ask a follow-up.

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.

2 participants