Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Phase 2: a comment is editable, by the person who wrote it

79c88d72d36d8974dca5859125f8f37f0b06c1f7 · Keenan Nemetz · 17d ago · parent 67dd8854

Phase 2: a comment is editable, by the person who wrote it

Comment editing was the last open follow-up on Phase 2, and the workaround was
"append another comment, or rewrite history and force-push" — a redaction tool
used for a typo. An edit is now an ordinary commit that rewrites the comment's
file in place: the path stays, so the id, the thread order and one `git log -p --
issues/<id>/comments/<cid>.md` that answers "what did it say" all survive. A
delete removes the file and leaves the text in history.

No `edited` field, for the same reason `updated` was dropped from issues: it
would be a line rewritten on every edit, and the commit that changed the file
already carries the time. Editing also does not re-surface anything — the feed
question is a timestamp, not a field.

Ownership is the interesting part. A comment's author is a name in a file, and a
name is not authentication, so `pre-receive` compares the author on the OLD blob
against the account the key resolved to. The old blob is the one that was
validated when it landed; the new one is whatever the pusher typed, and reading
it would let anyone adopt a comment by editing its author line in the same push.
Names are never reissued, so a former name still resolves through the roster and
an account keeps its own comments across a rename. An admin may override — a
thread written by somebody who has left is otherwise unrepairable — and the
override is written to data/audit.log, like a force-push or a release rewrite.

Two things are deliberately outside the rule:

- Adding a comment. A new file's author is a claim, exactly like an issue's
  `author`, and the boundary has never pretended otherwise.
- The issue file itself. `state`, `labels` and `assignees` are shared fields, so
  closing or assigning somebody else's issue is triage; refusing it would make
  the fields useless, and it is the same write any writer could already do by
  hand. A description can therefore be corrected by any writer — unchanged
  behaviour, now stated.

`vole issue edit` (title, body, labels, assignees) covers the issue file, and
`issue comment` now prints the comment id it created while `issue show` and the
web page print it too — otherwise `edit-comment` would take an id nobody could
see. Two comments written in the same millisecond share their ULID timestamp
prefix, so a short prefix is ambiguous there; the CLI names both matches instead
of guessing.

Also fixed on the way: `frontmatter.Value` reads one field without strict
decoding. `AuthorOf` first used Decode, whose KnownFields is right for validation
but wrong for "who wrote this" — the hook refused every comment edit with
`field created not found`.

Verified: TestCommentEditsBelongToTheirAuthor pushes as two accounts and drives
all four refusals (rewrite, adopt-by-author-line, delete, and the untouched
thread afterwards), plus an author editing his own comment, a writer closing
someone else's issue, and an admin delete that lands only with its audit line.
TestAuthorOfReadsAnyMetadataFile covers the strict-decoder bug directly;
TestCommentPathMatchesTheReader pins the writer/validator path agreement; the CLI
end-to-end test edits a comment, retitles an issue and deletes the comment
through the binary against a real instance. scripts/ci.sh green (gofmt, vet,
build, markdown links, go test -race ./...).

README.md +18−1

@@ -146,7 +146,10 @@ Or use the CLI, which is the same thing with the paperwork done:
146 146
147 147 ```sh
148 148 vole issue new --title "Nothing renders on the docs page" -m "Steps..." -l bug -a wilbur
149 vole issue comment -m "Also affects search" 01M5JR2JK29KKMAA1T9MDNTPBP
149 vole issue comment -m "Also affects search" 01M5JR2JK29KKMAA1T9MDNTPBP # prints the comment id
150 vole issue edit-comment 01M5JR2JK29KKMAA1T9MDNTPBP 01M5N1PRC4GJPZNEEG8PR8J4D6 -m "Corrected."
151 vole issue rm-comment 01M5JR2JK29KKMAA1T9MDNTPBP 01M5N1PRC4GJPZNEEG8PR8J4D6
152 vole issue edit 01M5JR2JK29KKMAA1T9MDNTPBP --title "A better title" -l bug -l docs
150 153 vole issue assign 01M5JR2JK29KKMAA1T9MDNTPBP wilbur
151 154 vole issue unassign 01M5JR2JK29KKMAA1T9MDNTPBP wilbur
152 155 vole issue close 01M5JR2JK29KKMAA1T9MDNTPBP
@@ -177,6 +180,20 @@ frontmatter. Closing an issue is editing `state` to `closed`. The push is
177 180 rejected with the offending path named if the tree does not match the format, and
178 181 every issue is a normal commit you can `git log`, `git blame` and merge.
179 182
183 Editing is a commit, not a special operation. Rewriting a comment's body keeps
184 its file, so `git log -p -- issues/<id>/comments/<ulid>.md` is what it used to
185 say; deleting one removes the file, and the text stays in history. Neither adds
186 an `edited` field — the commit that changed the file carries the time.
187
188 A comment belongs to whoever wrote it: `pre-receive` compares the author recorded
189 on the **old** blob (the one that was validated when it landed) against the
190 account the SSH key resolved to, so only that person can change or delete it, and
191 editing the author line in the same push does not help. A repo admin may override
192 — a thread written by someone who has left is otherwise unrepairable — and that
193 override is written to `data/audit.log`. The issue's own file is not covered:
194 closing, relabelling and assigning somebody else's issue is triage, not
195 authorship. Deleting is not redaction; redaction is still an admin force-push.
196
180 197 ## Reviewing a change
181 198
182 199 Review is a git artifact, not a web session. An approval records the exact commit