Range-diff !496

back to !496 Architecture review small fixes: FK check, web toggles, doc drift

 1:  8deb61e =  1:  9002a0b control: move the feed-line sentence renderer from httpd, so the CLI can share it
 2:  e6ca600 =  2:  e0ae401 feed: a labelled event names the labels
 3:  eab26fa =  3:  cf66fc3 dashboard, feed: render activity as the web's sentence, not the raw payload
 4:  b1c22ec =  4:  6dae104 feedline: extract FeedLine.Sentence, dedupe runDashboard/runFeed
 5:  b4721eb =  5:  2737992 dashboard: an assigned issue no longer repeats under open issues
 6:  584402d =  6:  b39ffae notifications list: name --all when the empty inbox is just read items
 7:  045a2d4 =  7:  061bd06 feedline: space between the repository and a tag, job or sha
 8:  575430b =  8:  3815908 notifications list: name --all only when read items exist
 9:  38a0c6e =  9:  45230cb e2e: an assigned issue is not listed under open_issues
10:  840738b ! 10:  d2db7d8 changelog: unreleased entry for #265
    @@ Commit message
         Ref #265
     
      ## CHANGELOG.org ##
    -@@ CHANGELOG.org: Versioning follows semver from v0.1.0. Database migrations run
    - automatically on daemon start; upgrade notes appear per release when
    - anything beyond "replace the binary and restart" is needed.
    - 
    -+* Unreleased
    -+
    +@@ CHANGELOG.org: must add =--scope full=. Existing tokens keep their scope.
    +   separately (#275).
    + - Audit retention deletes by id, up to the newest row older than the
    +   retention, so a clock step back cannot leave a gap in the chain (#275).
     +- =dashboard= and =feed= print activity as sentences
     +  (=cmc opened issue krz/gitbay#12=) instead of raw event payloads,
     +  and a labelled event names its labels there and on the web feed. An
    @@ CHANGELOG.org: Versioning follows semver from v0.1.0. Database migrations run
     +  =open_issues= field of =dashboard --json= no longer includes issues
     +  assigned to the caller. =notifications list= names =--all= when only
     +  read items remain (#265).
    -+
    + 
      * v1.36.0 — 2026-09-23
      
    - Terminal output for the CLI (#254).
11:  5d3cac6 = 11:  a3fa0f0 usage, help: print the program and the CLI's own path for the command
12:  83a68da = 12:  a8523b1 cli: send --path= where the CLI path differs from the server path
13:  59423f3 = 13:  a1e67e4 e2e: usage prints the CLI's own path, stock ssh the registered one
14:  3b8088c ! 14:  d905c69 flags: print a bad-flag usage line the way a usage refusal does
    @@ internal/control/control_test.go: func TestArgumentRefusalsNameTheUsage(t *testi
      // never over HTTP.
      func TestTermArgument(t *testing.T) {
     
    + ## internal/control/deploykey.go ##
    +@@ internal/control/deploykey.go: func init() {
    + }
    + 
    + func runDeployKeyAdd(c *Ctx, args []string) int {
    +-	f, err := parseFlags(args, flagSpec{Values: []string{"--ttl"}, Bools: []string{"--rw"}, MaxPos: 1, Usage: c.Cmd.Usage})
    ++	f, err := c.parseArgs(args, flagSpec{Values: []string{"--ttl"}, Bools: []string{"--rw"}, MaxPos: 1, Usage: c.Cmd.Usage})
    + 	if err != nil {
    + 		return c.fail(protocol.ExitUsage, "%v", err)
    + 	}
    +
      ## internal/control/diffcomment.go ##
     @@ internal/control/diffcomment.go: func init() {
      }
    @@ internal/control/help.go: func (c *Ctx) helpVerb(w io.Writer, cmd Command, below
      	}
     
      ## internal/control/identity.go ##
    -@@ internal/control/identity.go: func keyLabel(s string) (string, error) {
    +@@ internal/control/identity.go: func expiresText(t *time.Time, now time.Time) string {
      }
      
      func runKeysAdd(c *Ctx, args []string) int {
    --	f, err := parseFlags(args, flagSpec{Values: []string{"--scope", "--label"}, MaxPos: 0, Usage: "keys add [--scope full|git|runner] [--label <text>] < key.pub"})
    -+	f, err := c.parseArgs(args, flagSpec{Values: []string{"--scope", "--label"}, MaxPos: 0, Usage: "keys add [--scope full|git|runner] [--label <text>] < key.pub"})
    +-	f, err := parseFlags(args, flagSpec{Values: []string{"--scope", "--label", "--ttl"}, MaxPos: 0, Usage: c.Cmd.Usage})
    ++	f, err := c.parseArgs(args, flagSpec{Values: []string{"--scope", "--label", "--ttl"}, MaxPos: 0, Usage: c.Cmd.Usage})
      	if err != nil {
      		return c.fail(protocol.ExitUsage, "%v", err)
      	}
    @@ internal/control/thread.go: func runComment(c *Ctx, args []string, t thread, nou
      		return c.fail(protocol.ExitUsage, "%v", err)
     
      ## internal/control/token.go ##
    -@@ internal/control/token.go: func parseTTL(s string) (time.Duration, error) {
    +@@ internal/control/token.go: func (c *Ctx) ttlFlag(f flags) (*time.Time, int) {
      }
      
      func runTokenCreate(c *Ctx, args []string) int {
    --	f, err := parseFlags(args, flagSpec{Values: []string{"--name", "--scope", "--ttl"}, MaxPos: 0, Usage: "token create --name <n> [--scope full|read] [--ttl 30d]"})
    -+	f, err := c.parseArgs(args, flagSpec{Values: []string{"--name", "--scope", "--ttl"}, MaxPos: 0, Usage: "token create --name <n> [--scope full|read] [--ttl 30d]"})
    +-	f, err := parseFlags(args, flagSpec{Values: []string{"--name", "--scope", "--ttl"}, MaxPos: 0, Usage: c.Cmd.Usage})
    ++	f, err := c.parseArgs(args, flagSpec{Values: []string{"--name", "--scope", "--ttl"}, MaxPos: 0, Usage: c.Cmd.Usage})
    + 	if err != nil {
    + 		return c.fail(protocol.ExitUsage, "%v", err)
    + 	}
    +@@ internal/control/token.go: func runTokenList(c *Ctx, args []string) int {
    + }
    + 
    + func runTokenRevoke(c *Ctx, args []string) int {
    +-	f, err := parseFlags(args, flagSpec{Bools: []string{"--created"}, MaxPos: 1, Usage: c.Cmd.Usage})
    ++	f, err := c.parseArgs(args, flagSpec{Bools: []string{"--created"}, MaxPos: 1, Usage: c.Cmd.Usage})
      	if err != nil {
      		return c.fail(protocol.ExitUsage, "%v", err)
      	}
15:  d8aed5e = 15:  44191b1 auth keys add, pgp add: check --help before reading stdin
16:  5644d44 = 16:  4eb0f95 help: auth (and any future CLI-only grouping) renders with the registry layout, in the CLI's own paths
17:  1729fab = 17:  7664da8 auth --help: force --path= for a bare CLI-only alias group
18:  53ed395 = 18:  d5968a8 summaries: verb phrases instead of bare nouns
19:  3e62e73 = 19:  004ff6f control, cmd/gitbay: guard nounAliases and aliasGroupNames against drift
20:  34193bf ! 20:  ba0a7d3 changelog: #267 and the --path= upgrade note
    @@ Commit message
         Closes #267
     
      ## CHANGELOG.org ##
    -@@ CHANGELOG.org: anything beyond "replace the binary and restart" is needed.
    - 
    - * Unreleased
    - 
    +@@ CHANGELOG.org: must add =--scope full=. Existing tokens keep their scope.
    +   separately (#275).
    + - Audit retention deletes by id, up to the newest row older than the
    +   retention, so a clock step back cannot leave a gap in the chain (#275).
     +*Upgrade note.* Upgrade the instance before the CLI: an older server
     +refuses the CLI's leading =--path== argument as an unknown command,
     +for the eighteen commands whose CLI path differs from the registry's
     +(the =gitbay auth ...= commands and =repo topics list=).
    -+
      - =dashboard= and =feed= print activity as sentences
        (=cmc opened issue krz/gitbay#12=) instead of raw event payloads,
        and a labelled event names its labels there and on the web feed. An
    -@@ CHANGELOG.org: anything beyond "replace the binary and restart" is needed.
    +@@ CHANGELOG.org: must add =--scope full=. Existing tokens keep their scope.
        =open_issues= field of =dashboard --json= no longer includes issues
        assigned to the caller. =notifications list= names =--all= when only
        read items remain (#265).
21:  8a07a1a = 21:  fd270da help: no auth <verb> usage or footer over stock ssh
22:  217efb8 = 22:  bc76b6f changelog: usage refusals name ssh git@<host> outside the CLI
23:  26a34f1 = 23:  3595439 sshd: unregistered-key message names the fingerprint and the real host
24:  9b753b2 = 24:  e3ba425 issue create: --label, --milestone, --assignee
25:  e862c75 = 25:  ea552d4 mr show: pluralize commits/checks/reviews section headings
26:  e511d9a = 26:  86c82f6 repo readme: print a repository's README, the web page's file order
27:  da82219 = 27:  6bf4591 repo show: truncate the mirror's last-sync time to the second
28:  3640d2c = 28:  4fa1930 issue create: resolve milestone and assignees first, set fields through issue label/milestone/assign
29:  5218bc7 ! 29:  79fdea2 sshd: unregistered-key settings link from the site URL, scheme and port kept
    @@ Commit message
     
      ## internal/sshd/sshd.go ##
     @@ internal/sshd/sshd.go: import (
    - 	"os"
      	"path/filepath"
    + 	"slices"
      	"strconv"
     +	"strings"
      	"sync"
30:  3e84bde = 30:  a4152dd wiki: issue create synopsis names --label, --milestone, --assignee
31:  21ea04d = 31:  0338e6a store: run foreign_key_check inside the migration transaction, before commit
32:  a40cff9 = 32:  d0e26d3 web: pin and watch toggles dispatch through repo pin/watch/mute/unwatch
33:  4b0b4c0 = 33:  8a379d4 web: Cache-Control: no-store on the login-link request
34:  0fa9475 = 34:  4ffdc2c wiki: fix API token-refusal claim, batched-review status, watch/pin dispatch, login-link URL exception