Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package klog for openSUSE:Factory checked in 
at 2026-10-20 12:43:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/klog (Old)
 and      /work/SRC/openSUSE:Factory/.klog.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "klog"

Tue Oct 20 12:43:57 2026 rev:7 rq:1386422 version:7.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/klog/klog.changes        2026-02-25 
21:21:31.569224800 +0100
+++ /work/SRC/openSUSE:Factory/.klog.new.3698736/klog.changes   2026-10-20 
12:44:03.875907571 +0200
@@ -1,0 +2,25 @@
+Tue Oct 20 07:06:17 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 7.2:
+  * [ NOTICE ] Some (potentially build-breaking!) updates around
+    the build process:
+    - The naming scheme of the zipped release artifacts have
+      changed, it’s now: klog-${OS}-${ARCH}64.zip, e.g.:
+      klog-linux-amd64.zip. (The binary inside the zip archive is
+      still named klog / klog.exe, just as before.) There are now
+      also ARM64 builds for both Linux and Windows available.
+    - There is a new Go build flag BinaryBuildDate, which embeds
+      and renders the build date in the klog version subcommand.
+    - All 3 Go build flags (BinaryVersion, BinaryBuildHash,
+      BinaryBuildDate) now undergo some basic validation – see
+      klog.go to learn more. Malformed or absent values don’t break
+      the build, but are gracefully substituted by placeholder
+      values, e.g. v?.?.
+  * [ FEATURE ] Add new klog note subcommand for appending text to
+    existing entry summaries. (See klog note --help to learn more.)
+  * [ FEATURE ] Support --now flag on klog print subcommand
+    (primarily useful in conjunction with the --with-totals flag).
+  * [ FIX ] Tab completion now works reliable for file paths
+    containing spaces or other special characters.
+
+-------------------------------------------------------------------

Old:
----
  klog-7.1.obscpio

New:
----
  klog-7.2.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ klog.spec ++++++
--- /var/tmp/diff_new_pack.eunrgv/_old  2026-10-20 12:44:05.805988314 +0200
+++ /var/tmp/diff_new_pack.eunrgv/_new  2026-10-20 12:44:05.806988355 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           klog
-Version:        7.1
+Version:        7.2
 Release:        0
 Summary:        Time tracking in a human-readable, plain-text file format
 License:        MIT
@@ -27,7 +27,7 @@
 BuildRequires:  bash-completion
 BuildRequires:  fish
 BuildRequires:  zsh
-BuildRequires:  golang(API) >= 1.26
+BuildRequires:  golang(API) >= 1.27
 
 %description
 klog is a plain-text file format and a command line tool for time tracking.
@@ -76,11 +76,15 @@
 %build
 COMMIT_HASH="$(sed -n 's/commit: \(.*\)/\1/p' %_sourcedir/%{name}.obsinfo)"
 
+DATE_FMT="+%%Y-%%m-%%dT%%H:%%M:%%SZ"
+BUILD_DATE=$(date -u -d "@${SOURCE_DATE_EPOCH}" "${DATE_FMT}" 2>/dev/null || 
date -u -r "${SOURCE_DATE_EPOCH}" "${DATE_FMT}" 2>/dev/null || date -u 
"${DATE_FMT}")
+
 go build \
    -mod=vendor \
    -buildmode=pie \
    -ldflags=" \
    -X main.BinaryVersion=v%{version} \
+   -X main.BinaryBuildDate=${BUILD_DATE} \
    -X main.BinaryBuildHash=${COMMIT_HASH}" \
    -o bin/%{name} ./%{name}.go
 

++++++ _service ++++++
--- /var/tmp/diff_new_pack.eunrgv/_old  2026-10-20 12:44:05.838989694 +0200
+++ /var/tmp/diff_new_pack.eunrgv/_new  2026-10-20 12:44:05.841989820 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/jotaen/klog</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v7.1</param>
+    <param name="revision">v7.2</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>
@@ -11,7 +11,7 @@
   <service name="set_version" mode="manual">
   </service>
   <service name="go_modules" mode="manual">
-    <param name="basename">klog-7.1</param>
+    <param name="basename">klog-7.2</param>
   </service>
   <!-- services below are running at buildtime -->
   <service name="tar" mode="buildtime">

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.eunrgv/_old  2026-10-20 12:44:05.867990907 +0200
+++ /var/tmp/diff_new_pack.eunrgv/_new  2026-10-20 12:44:05.871991075 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/jotaen/klog</param>
-              <param 
name="changesrevision">7a4f7b6b0749adb164aa6ac67dee9ba6556f261e</param></service></servicedata>
+              <param 
name="changesrevision">66d62de2091364685ffd5226bf35f11104f9246b</param></service></servicedata>
 (No newline at EOF)
 

++++++ klog-7.1.obscpio -> klog-7.2.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/CHANGELOG.md new/klog-7.2/CHANGELOG.md
--- old/klog-7.1/CHANGELOG.md   2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/CHANGELOG.md   2026-10-19 19:23:47.000000000 +0200
@@ -1,6 +1,25 @@
 # Changelog
 **Summary of changes of the command line tool**
 
+## v7.2 (2026-10-26)
+- **[ NOTICE ]** Some (potentially build-breaking!) updates around the build 
process:
+  - The naming scheme of the zipped release artifacts have changed,
+    it’s now: `klog-${OS}-${ARCH}64.zip`, e.g.: `klog-linux-amd64.zip`. (The 
binary
+    inside the zip archive is still named `klog` / `klog.exe`, just as before.)
+    There are now also ARM64 builds for both Linux and Windows available.
+  - There is a new Go build flag `BinaryBuildDate`, which embeds and renders 
the
+    build date in the `klog version` subcommand.
+  - All 3 Go build flags (`BinaryVersion`, `BinaryBuildHash`, 
`BinaryBuildDate`)
+    now undergo some basic validation – see `klog.go` to learn more. Malformed 
or
+    absent values don’t break the build, but are gracefully substituted by 
placeholder
+    values, e.g. `v?.?`.
+- **[ FEATURE ]** Add new `klog note` subcommand for appending text to existing
+  entry summaries. (See `klog note --help` to learn more.)
+- **[ FEATURE ]** Support `--now` flag on `klog print` subcommand
+  (primarily useful in conjunction with the `--with-totals` flag).
+- **[ FIX ]** Tab completion now works reliable for file paths containing 
spaces
+  or other special characters.
+
 ## v7.1 (2026-02-22)
 - **[ FEATURE ]** Include warnings in JSON output of `klog json` subcommand.
 - **[ FEATURE ]** When using `klog report --fill` and combining this with a 
periodic
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/README.md new/klog-7.2/README.md
--- old/klog-7.1/README.md      2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/README.md      2026-10-19 19:23:47.000000000 +0200
@@ -23,6 +23,8 @@
 If you have questions, feedback, feature ideas, or want to report something 
that’s not working properly,
 feel invited to [start a 
discussion](https://github.com/jotaen/klog/discussions).
 
+To learn how to compile klog from sources, please refer to the `run::build` 
shell function in [the `run.sh` file](./run.sh).
+
 ## About
 
 klog was created by [Jan Heuermann](https://www.jotaen.net).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/go.mod new/klog-7.2/go.mod
--- old/klog-7.1/go.mod 2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/go.mod 2026-10-19 19:23:47.000000000 +0200
@@ -1,22 +1,20 @@
 module github.com/jotaen/klog
 
-go 1.26
+go 1.27
 
 require (
        cloud.google.com/go v0.123.0
-       github.com/alecthomas/kong v1.14.0
+       github.com/alecthomas/kong v1.16.1
        github.com/jotaen/genie v0.0.3
-       github.com/jotaen/kong-completion v0.0.12
+       github.com/jotaen/kong-completion v0.0.15
        github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
        github.com/posener/complete v1.2.3
-       github.com/stretchr/testify v1.11.1
+       github.com/stretchr/testify v1.12.1
 )
 
 require (
-       github.com/davecgh/go-spew v1.1.1 // indirect
        github.com/hashicorp/errwrap v1.1.0 // indirect
        github.com/hashicorp/go-multierror v1.1.1 // indirect
-       github.com/pmezard/go-difflib v1.0.0 // indirect
        github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // 
indirect
-       gopkg.in/yaml.v3 v3.0.1 // indirect
+       go.yaml.in/yaml/v3 v3.0.5 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/go.sum new/klog-7.2/go.sum
--- old/klog-7.1/go.sum 2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/go.sum 2026-10-19 19:23:47.000000000 +0200
@@ -2,13 +2,11 @@
 cloud.google.com/go v0.123.0/go.mod 
h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
 github.com/alecthomas/assert/v2 v2.11.0 
h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
 github.com/alecthomas/assert/v2 v2.11.0/go.mod 
h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
-github.com/alecthomas/kong v1.14.0 
h1:gFgEUZWu2ZmZ+UhyZ1bDhuutbKN1nTtJTwh19Wsn21s=
-github.com/alecthomas/kong v1.14.0/go.mod 
h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
+github.com/alecthomas/kong v1.16.1 
h1:ixhCt93XkJ98kGposQ54+bl0IK6XwqB40AsMynU7Z8E=
+github.com/alecthomas/kong v1.16.1/go.mod 
h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
 github.com/alecthomas/repr v0.5.2 
h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
 github.com/alecthomas/repr v0.5.2/go.mod 
h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/hashicorp/errwrap v1.0.0/go.mod 
h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -21,11 +19,10 @@
 github.com/hexops/gotextdiff v1.0.3/go.mod 
h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 github.com/jotaen/genie v0.0.3 h1:YyyEn4PMnmVwu9BYm3qCCW6mzlMzw6hNeAKzUmAP0HY=
 github.com/jotaen/genie v0.0.3/go.mod 
h1:5v0pWbZ+yHWL8QIfTq1PSuUuoGQ1enQZ4XTtV/PnJos=
-github.com/jotaen/kong-completion v0.0.12 
h1:a9jmSaWgkdAUMQT583UxLIJrO9tfdSmYqcIxrBByjPc=
-github.com/jotaen/kong-completion v0.0.12/go.mod 
h1:dyIG20e3qq128SUBtF8jzI7YtkfzjWMlgbqkAJd6xHQ=
+github.com/jotaen/kong-completion v0.0.15 
h1:ygxUE1S2Ec4OOnJxQEXDf8UdSQryrQzYksgEGjd8jVE=
+github.com/jotaen/kong-completion v0.0.15/go.mod 
h1:dyIG20e3qq128SUBtF8jzI7YtkfzjWMlgbqkAJd6xHQ=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 
h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod 
h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.2.3 
h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
 github.com/posener/complete v1.2.3/go.mod 
h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
@@ -33,10 +30,9 @@
 github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod 
h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.4.0/go.mod 
h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.11.1 
h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
-github.com/stretchr/testify v1.11.1/go.mod 
h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 
h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+github.com/stretchr/testify v1.12.1 
h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
+github.com/stretchr/testify v1.12.1/go.mod 
h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
+go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
+go.yaml.in/yaml/v3 v3.0.5/go.mod 
h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/args/now.go 
new/klog-7.2/klog/app/cli/args/now.go
--- old/klog-7.1/klog/app/cli/args/now.go       2026-02-22 11:46:19.000000000 
+0100
+++ new/klog-7.2/klog/app/cli/args/now.go       2026-10-19 19:23:47.000000000 
+0200
@@ -9,35 +9,35 @@
 )
 
 type NowArgs struct {
-       Now          bool `name:"now" short:"n" help:"Assume open ranges to be 
closed at this moment."`
-       hadOpenRange bool // Field only for internal use
+       Now           bool                `name:"now" short:"n" help:"Assume 
open ranges to be closed at this moment."`
+       closedEntries map[klog.Record]int // Field only for internal use
 }
 
-func (args *NowArgs) ApplyNow(reference gotime.Time, rs ...klog.Record) 
app.Error {
+func (args *NowArgs) ApplyNow(reference gotime.Time, rs ...klog.Record) 
(map[klog.Record]int, app.Error) {
        if args.Now {
-               hasClosedAnyRange, err := service.CloseOpenRanges(reference, 
rs...)
+               closedEntries, err := service.CloseOpenRanges(reference, rs...)
                if err != nil {
-                       return app.NewErrorWithCode(
+                       return nil, app.NewErrorWithCode(
                                app.LOGICAL_ERROR,
                                "Cannot apply --now flag",
                                "There are records with uncloseable time 
ranges",
                                err,
                        )
                }
-               args.hadOpenRange = hasClosedAnyRange
-               return nil
+               args.closedEntries = closedEntries
+               return closedEntries, nil
        }
-       return nil
+       return nil, nil
 }
 
 func (args *NowArgs) HadOpenRange() bool {
-       return args.hadOpenRange
+       return len(args.closedEntries) > 0
 }
 
 // GetWarning warns the user that they specified the --now flag but there 
actually
 // weren’t any closable ranges in the data.
 func (args *NowArgs) GetWarning() service.UsageWarning {
-       if args.Now && !args.hadOpenRange {
+       if args.Now && !args.HadOpenRange() {
                return service.PointlessNowWarning
        }
        return service.UsageWarning{}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/index.go 
new/klog-7.2/klog/app/cli/index.go
--- old/klog-7.1/klog/app/cli/index.go  2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/index.go  2026-10-19 19:23:47.000000000 +0200
@@ -36,6 +36,7 @@
        Pause  Pause  `cmd:"" name:"pause" group:"Manipulate Files" help:"Pause 
the open time range."`
        Switch Switch `cmd:"" name:"switch" group:"Manipulate Files" 
help:"Close open range and starts a new one."`
        Create Create `cmd:"" name:"create" group:"Manipulate Files" 
help:"Create a new, empty record."`
+       Note   Note   `cmd:"" name:"note" group:"Manipulate Files" help:"Append 
text to an existing entry summary."`
 
        // Manage Files
        Bookmarks Bookmarks `cmd:"" name:"bookmarks" group:"Manage Files" 
aliases:"bk" help:"Named aliases for often-used files."`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/json.go 
new/klog-7.2/klog/app/cli/json.go
--- old/klog-7.1/klog/app/cli/json.go   2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/json.go   2026-10-19 19:23:47.000000000 +0200
@@ -42,7 +42,7 @@
                return err
        }
        now := ctx.Now()
-       nErr := opt.ApplyNow(now, records...)
+       _, nErr := opt.ApplyNow(now, records...)
        if nErr != nil {
                return nErr
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/note.go 
new/klog-7.2/klog/app/cli/note.go
--- old/klog-7.1/klog/app/cli/note.go   1970-01-01 01:00:00.000000000 +0100
+++ new/klog-7.2/klog/app/cli/note.go   2026-10-19 19:23:47.000000000 +0200
@@ -0,0 +1,55 @@
+package cli
+
+import (
+       "github.com/jotaen/klog/klog"
+       "github.com/jotaen/klog/klog/app"
+       "github.com/jotaen/klog/klog/app/cli/args"
+       "github.com/jotaen/klog/klog/app/cli/helper"
+       "github.com/jotaen/klog/klog/parser/reconciling"
+)
+
+type Note struct {
+       Text       klog.EntrySummary `arg:"" required:"" placeholder:"TEXT" 
help:"The text to add."`
+       Entry      int               `name:"entry" short:"e" placeholder:"INT" 
help:"Target the nth entry instead of the open time range. If INT is positive, 
it counts from the start (beginning with '1'); if negative, it counts from the 
end (beginning with '-1'). If absent, it tries to find and target the 
open-ended entry."`
+       AppendMode string            `name:"append-mode" short:"a" 
placeholder:"MODE" help:"How to add the text: MODE can be 'inline' (default) or 
'newline'." enum:"inline,newline" default:"inline"`
+       args.AtDateArgs
+       args.NoStyleArgs
+       args.WarnArgs
+       args.OutputFileArgs
+}
+
+func (opt *Note) Help() string {
+       return `
+By default, it targets the open time range (e.g. '18:00 - ?'). Alternatively, 
you can choose any entry via '--entry'.
+
+The '--append-mode' flag controls how the text is added:
+  - '--append-mode=inline' (default) adds the text to the last line of the 
summary, delimited by a space character.
+  - '--append-mode=newline' adds the text as separate line underneath the 
summary.
+
+By default, the command targets the record at today’s date, or yesterday’s if 
there is no record today.
+You can otherwise specify a date with '--date'.
+`
+}
+
+func (opt *Note) Run(ctx app.Context) app.Error {
+       opt.NoStyleArgs.Apply(&ctx)
+       date := opt.AtDate(ctx.Now())
+       creators := 
[]reconciling.Creator{reconciling.NewReconcilerAtRecord(date)}
+       if opt.Date == nil {
+               // If no date had been specified explicitly, try to fall back 
to yesterday’s record.
+               creators = append(creators, 
reconciling.NewReconcilerAtRecord(date.PlusDays(-1)))
+       }
+       summary := opt.Text
+       if opt.AppendMode == "newline" && summary[0] != "" {
+               summary = append(klog.EntrySummary{""}, summary...)
+       }
+       return helper.Reconcile(ctx, helper.ReconcileOpts{OutputFileArgs: 
opt.OutputFileArgs, WarnArgs: opt.WarnArgs},
+               creators,
+               func(reconciler *reconciling.Reconciler) error {
+                       if opt.Entry != 0 {
+                               return reconciler.AppendToNthEntry(opt.Entry, 
summary)
+                       }
+                       return reconciler.AppendToOpenRange(summary)
+               },
+       )
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/note_test.go 
new/klog-7.2/klog/app/cli/note_test.go
--- old/klog-7.1/klog/app/cli/note_test.go      1970-01-01 01:00:00.000000000 
+0100
+++ new/klog-7.2/klog/app/cli/note_test.go      2026-10-19 19:23:47.000000000 
+0200
@@ -0,0 +1,202 @@
+package cli
+
+import (
+       "testing"
+
+       "github.com/jotaen/klog/klog"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestNoteAppendsInlineToOpenRange(t *testing.T) {
+       t.Run("no existing summary", func(t *testing.T) {
+               state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00 - ?
+       1h Other
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+                       Text: klog.Ɀ_EntrySummary_("Working on something #tag"),
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       9:00 - ? Working on something #tag
+       1h Other
+`, state.writtenFileContents)
+       })
+
+       t.Run("existing single-line summary", func(t *testing.T) {
+               state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00-? Working
+       1h Other
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+                       Text: klog.Ɀ_EntrySummary_("on something #tag"),
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       9:00-? Working on something #tag
+       1h Other
+`, state.writtenFileContents)
+       })
+
+       t.Run("existing multiline summary", func(t *testing.T) {
+               state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00-? Working
+               on something
+       1h Other
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+                       Text: klog.Ɀ_EntrySummary_("#tag"),
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       9:00-? Working
+               on something #tag
+       1h Other
+`, state.writtenFileContents)
+       })
+}
+
+func TestNoteAddsNewLineToOpenRange(t *testing.T) {
+       t.Run("no existing summary", func(t *testing.T) {
+               state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00-?
+       1h Other
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+                       Text:       klog.Ɀ_EntrySummary_("Did something #tag"),
+                       AppendMode: "newline",
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       9:00-?
+               Did something #tag
+       1h Other
+`, state.writtenFileContents)
+       })
+
+       t.Run("existing single-line summary", func(t *testing.T) {
+               state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00-? Working
+       1h Other
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+                       Text:       klog.Ɀ_EntrySummary_("Did something #tag"),
+                       AppendMode: "newline",
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       9:00-? Working
+               Did something #tag
+       1h Other
+`, state.writtenFileContents)
+       })
+
+       t.Run("existing multiline summary", func(t *testing.T) {
+               state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00-? Working
+               More Working
+       1h Other
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+                       Text:       klog.Ɀ_EntrySummary_("Did something #tag"),
+                       AppendMode: "newline",
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       9:00-? Working
+               More Working
+               Did something #tag
+       1h Other
+`, state.writtenFileContents)
+       })
+}
+
+func TestNoteTargetsNthEntry(t *testing.T) {
+       records := `
+1920-02-02
+       8:00-9:00 First
+       9:00-? Second
+       1h Third
+`
+       t.Run("Counting from the start", func(t *testing.T) {
+               state, err := 
NewTestingContext()._SetRecords(records)._SetNow(1920, 2, 2, 15, 
24)._Run((&Note{
+                       Text:       klog.Ɀ_EntrySummary_("Note"),
+                       Entry:      1,
+                       AppendMode: "newline",
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       8:00-9:00 First
+               Note
+       9:00-? Second
+       1h Third
+`, state.writtenFileContents)
+       })
+
+       t.Run("Counting from the end", func(t *testing.T) {
+               state, err := 
NewTestingContext()._SetRecords(records)._SetNow(1920, 2, 2, 15, 
24)._Run((&Note{
+                       Text:  klog.Ɀ_EntrySummary_("and more"),
+                       Entry: -1,
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+1920-02-02
+       8:00-9:00 First
+       9:00-? Second
+       1h Third and more
+`, state.writtenFileContents)
+       })
+
+       t.Run("Out of range", func(t *testing.T) {
+               for _, entry := range []int{4, -4} {
+                       _, err := 
NewTestingContext()._SetRecords(records)._SetNow(1920, 2, 2, 15, 
24)._Run((&Note{
+                               Text:  klog.Ɀ_EntrySummary_("Note"),
+                               Entry: entry,
+                       }).Run)
+                       require.Error(t, err)
+               }
+       })
+}
+
+func TestNoteFallsBackToYesterday(t *testing.T) {
+       state, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       22:00-?
+`)._SetNow(1920, 2, 3, 1, 10)._Run((&Note{
+               Text: klog.Ɀ_EntrySummary_("Still working"),
+       }).Run)
+       require.Nil(t, err)
+       assert.Equal(t, `
+1920-02-02
+       22:00-? Still working
+`, state.writtenFileContents)
+}
+
+func TestNoteDoesNotFallBackToYesterdayIfDateSpecified(t *testing.T) {
+       _, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       22:00-?
+`)._SetNow(1920, 2, 3, 1, 10)._Run((&Note{
+               Text: klog.Ɀ_EntrySummary_("Still working"),
+               Date: klog.Ɀ_Date_(1920, 2, 3),
+       }).Run)
+       require.Error(t, err)
+}
+
+func TestNoteFailsWithoutOpenRange(t *testing.T) {
+       _, err := NewTestingContext()._SetRecords(`
+1920-02-02
+       9:00-10:00
+`)._SetNow(1920, 2, 2, 15, 24)._Run((&Note{
+               Text: klog.Ɀ_EntrySummary_("Text"),
+       }).Run)
+       require.Error(t, err)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/print.go 
new/klog-7.2/klog/app/cli/print.go
--- old/klog-7.1/klog/app/cli/print.go  2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/print.go  2026-10-19 19:23:47.000000000 +0200
@@ -15,6 +15,7 @@
        WithTotals bool `name:"with-totals" help:"Amend output with evaluated 
total times."`
        args.FilterArgs
        args.SortArgs
+       args.NowArgs
        args.WarnArgs
        args.NoStyleArgs
        args.InputFilesArgs
@@ -26,6 +27,10 @@
 Note that the output doesn’t resemble the file verbatim, but it may apply some 
minor formatting.
 
 You can optionally also sort the records, or print out the total times for 
each record and entry.
+
+Open-ended time ranges (e.g., '8:00 - ?') are printed as they are, and they 
count as '0m' in the totals when using the '--with-totals' flag.
+With the '--now' flag, they are printed as if they were closed “right now”, 
and their elapsed time is factored into the totals.
+In the totals column, the provisional durations are marked with an asterisk, 
e.g. '2h17m*'.
 `
 }
 
@@ -45,22 +50,29 @@
                return nil
        }
        records = opt.ApplySort(records)
+       closedEntries, nErr := opt.ApplyNow(now, records...)
+       if nErr != nil {
+               return nErr
+       }
        serialisedRecords := parser.SerialiseRecords(serialser, records...)
        output := func() string {
                if opt.WithTotals {
-                       return printWithDurations(styler, serialisedRecords)
+                       return printWithDurations(styler, serialisedRecords, 
closedEntries)
                }
                return "\n" + serialisedRecords.ToString()
        }()
        ctx.Print(output + "\n")
 
-       opt.WarnArgs.PrintWarnings(ctx, records, nil)
+       opt.WarnArgs.PrintWarnings(ctx, records, 
[]service.UsageWarning{opt.NowArgs.GetWarning()})
        return nil
 }
 
-func printWithDurations(styler tf.Styler, ls parser.Lines) string {
+// printWithDurations prefixes each line with its total duration. 
`closedEntries`
+// denotes the entries whose open range was closed via `--now`; the 
auto-closed durations
+// are suffixed with an asterisk, to indicate that they are provisional.
+func printWithDurations(styler tf.Styler, ls parser.Lines, closedEntries 
map[klog.Record]int) string {
        type Prefix struct {
-               d     klog.Duration
+               text  string
                isSub bool
        }
        var prefixes []*Prefix
@@ -76,18 +88,39 @@
                        }
                        if previousRecord == nil {
                                previousRecord = l.Record
-                               return &Prefix{service.Total(l.Record), false}
+                               return &Prefix{
+                                       text: func() string {
+                                               suffix := ""
+                                               _, hasAutoClosedEntry := 
closedEntries[l.Record]
+                                               if hasAutoClosedEntry {
+                                                       suffix = "*"
+                                               }
+                                               return 
service.Total(l.Record).ToString() + suffix
+                                       }(),
+                                       isSub: false,
+                               }
                        }
                        if l.EntryI != -1 && l.EntryI != previousEntry {
                                previousEntry = l.EntryI
-                               return 
&Prefix{l.Record.Entries()[l.EntryI].Duration(), true}
+                               return &Prefix{
+                                       text: func() string {
+                                               openI, hasAutoClosedEntry := 
closedEntries[l.Record]
+                                               entryTotal := 
l.Record.Entries()[l.EntryI].Duration()
+                                               suffix := ""
+                                               if hasAutoClosedEntry && openI 
== l.EntryI {
+                                                       suffix = "*"
+                                               }
+                                               return entryTotal.ToString() + 
suffix
+                                       }(),
+                                       isSub: true,
+                               }
                        } else {
                                return nil
                        }
                }()
                prefixes = append(prefixes, prefix)
-               if prefix != nil && len(prefix.d.ToString()) > maxColumnLength {
-                       maxColumnLength = len(prefix.d.ToString())
+               if prefix != nil && len(prefix.text) > maxColumnLength {
+                       maxColumnLength = len(prefix.text)
                }
        }
 
@@ -102,12 +135,12 @@
                        if p == nil {
                                return strings.Repeat(" ", maxColumnLength+1)
                        }
-                       length := len(p.d.ToString())
+                       length := len(p.text)
                        value := ""
                        if p.isSub {
-                               value += styler.Props(tf.StyleProps{Color: 
tf.TEXT_SUBDUED}).Format(p.d.ToString())
+                               value += styler.Props(tf.StyleProps{Color: 
tf.TEXT_SUBDUED}).Format(p.text)
                        } else {
-                               value += 
styler.Props(tf.StyleProps{IsUnderlined: true}).Format(p.d.ToString())
+                               value += 
styler.Props(tf.StyleProps{IsUnderlined: true}).Format(p.text)
                        }
                        return strings.Repeat(" ", maxColumnLength-length+1) + 
value
                }()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/print_test.go 
new/klog-7.2/klog/app/cli/print_test.go
--- old/klog-7.1/klog/app/cli/print_test.go     2026-02-22 11:46:19.000000000 
+0100
+++ new/klog-7.2/klog/app/cli/print_test.go     2026-10-19 19:23:47.000000000 
+0200
@@ -80,7 +80,7 @@
        assert.Equal(t, "\n2018-02-01\n\n2018-01-31\n\n2018-01-30\n\n", 
stateSortedDesc.printBuffer)
 }
 
-func TestPrintRecordsWithDurations(t *testing.T) {
+func TestPrintRecordsWithTotals(t *testing.T) {
        state, err := NewTestingContext()._SetNow(2018, 02, 07, 19, 
00)._SetRecords(`
 2018-01-31
 Hello #world
@@ -122,3 +122,83 @@
 
 `, state.printBuffer)
 }
+
+func TestPrintRecordsWithNow(t *testing.T) {
+       records := `
+2018-02-05
+No open entry
+    5h
+
+2018-02-06
+       22:00 - ? Late shift
+
+2018-02-07
+       35m
+       18:00 - ? I just
+               started something
+`
+
+       t.Run("without --with-totals", func(t *testing.T) {
+               state, err := NewTestingContext()._SetNow(2018, 02, 07, 19, 
00)._SetRecords(records)._Run((&Print{
+                       NowArgs: args.NowArgs{Now: true},
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+2018-02-05
+No open entry
+    5h
+
+2018-02-06
+    22:00 - 19:00> Late shift
+
+2018-02-07
+    35m
+    18:00 - 19:00 I just
+        started something
+
+`, state.printBuffer)
+       })
+
+       t.Run("together with --with-totals", func(t *testing.T) {
+               state, err := NewTestingContext()._SetNow(2018, 02, 07, 19, 
00)._SetRecords(records)._Run((&Print{
+                       WithTotals: true,
+                       NowArgs:    args.NowArgs{Now: true},
+               }).Run)
+               require.Nil(t, err)
+               assert.Equal(t, `
+     5h  |  2018-02-05
+         |  No open entry
+     5h  |      5h
+
+   21h*  |  2018-02-06
+   21h*  |      22:00 - 19:00> Late shift
+
+ 1h35m*  |  2018-02-07
+    35m  |      35m
+    1h*  |      18:00 - 19:00 I just
+         |          started something
+
+`, state.printBuffer)
+       })
+}
+
+func TestPrintWithNowWarnsIfNoOpenRange(t *testing.T) {
+       state, err := NewTestingContext()._SetNow(2018, 02, 07, 19, 
00)._SetRecords(`
+2018-02-07
+       35m
+`)._Run((&Print{
+               NowArgs: args.NowArgs{Now: true},
+       }).Run)
+       require.Nil(t, err)
+       assert.Contains(t, state.printBuffer, "--now")
+}
+
+func TestPrintWithNowFailsOnUncloseableRange(t *testing.T) {
+       _, err := NewTestingContext()._SetNow(2018, 02, 07, 19, 
00)._SetRecords(`
+2018-01-01
+       8:00 - ?
+`)._Run((&Print{
+               NowArgs: args.NowArgs{Now: true},
+       }).Run)
+       require.Error(t, err)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/report.go 
new/klog-7.2/klog/app/cli/report.go
--- old/klog-7.1/klog/app/cli/report.go 2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/report.go 2026-10-19 19:23:47.000000000 +0200
@@ -57,7 +57,7 @@
        if len(records) == 0 {
                return nil
        }
-       nErr := opt.ApplyNow(now, records...)
+       _, nErr := opt.ApplyNow(now, records...)
        if nErr != nil {
                return nErr
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/tags.go 
new/klog-7.2/klog/app/cli/tags.go
--- old/klog-7.1/klog/app/cli/tags.go   2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/tags.go   2026-10-19 19:23:47.000000000 +0200
@@ -47,7 +47,7 @@
        if fErr != nil {
                return fErr
        }
-       nErr := opt.ApplyNow(now, records...)
+       _, nErr := opt.ApplyNow(now, records...)
        if nErr != nil {
                return nErr
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/today.go 
new/klog-7.2/klog/app/cli/today.go
--- old/klog-7.1/klog/app/cli/today.go  2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/today.go  2026-10-19 19:23:47.000000000 +0200
@@ -66,7 +66,7 @@
                return err
        }
        now := ctx.Now()
-       nErr := opt.ApplyNow(now, records...)
+       _, nErr := opt.ApplyNow(now, records...)
        if nErr != nil {
                return nErr
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/total.go 
new/klog-7.2/klog/app/cli/total.go
--- old/klog-7.1/klog/app/cli/total.go  2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/cli/total.go  2026-10-19 19:23:47.000000000 +0200
@@ -40,7 +40,7 @@
        if fErr != nil {
                return fErr
        }
-       nErr := opt.ApplyNow(now, records...)
+       _, nErr := opt.ApplyNow(now, records...)
        if nErr != nil {
                return nErr
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/cli/version.go 
new/klog-7.2/klog/app/cli/version.go
--- old/klog-7.1/klog/app/cli/version.go        2026-02-22 11:46:19.000000000 
+0100
+++ new/klog-7.2/klog/app/cli/version.go        2026-10-19 19:23:47.000000000 
+0200
@@ -45,7 +45,7 @@
                return nil
        }
        ctx.Print("Command line tool: " + ctx.Meta().Version)
-       ctx.Print("  [" + ctx.Meta().SrcHash + "]\n")
+       ctx.Print(fmt.Sprintf("  [%s, %s]\n", ctx.Meta().SrcHash, 
ctx.Meta().BuildDate))
        ctx.Print("File format: version " + klog.SPEC_VERSION + "\n")
 
        if opt.NoCheck {
@@ -75,7 +75,7 @@
        if v.Version() == ctx.Meta().Version && ctx.Meta().SrcHash == 
v.SrcHash() {
                ctx.Print("You already have the latest version!\n")
        } else {
-               ctx.Print(fmt.Sprintf("New version available: %s  [%s]\n", 
v.Version(), v.SrcHash()))
+               ctx.Print(fmt.Sprintf("New version available: %s  [%s, %s]\n", 
v.Version(), v.SrcHash(), v.BuildDate()))
                downloadLinkPath := ""
                if v.DownloadLinkPath() != "" {
                        downloadLinkPath = "/" + v.DownloadLinkPath()
@@ -88,6 +88,7 @@
 type versionInfo interface {
        Version() string
        SrcHash() string
+       BuildDate() string
        DownloadLinkPath() string
        IsValid() bool
 }
@@ -138,10 +139,11 @@
        return nil, nil
 }
 
-// versionFromJotaen checks the version from klog.jotaen.net
+// versionFromJotaen checks the version via klog.jotaen.net
 type versionFromJotaen struct {
        Version_          string `json:"version"`
        BuildHash_        string `json:"src_hash"`
+       ReleaseDate_      string `json:"release_date"`
        DownloadLinkPath_ string `json:"download_link_path"`
 }
 
@@ -156,11 +158,13 @@
 func (v *versionFromJotaen) IsValid() bool {
        return strings.HasPrefix(v.Version_, "v") && len(v.BuildHash_) >= 7
 }
+func (v *versionFromJotaen) BuildDate() string { return v.ReleaseDate_ }
 
-// versionFromGithub checks the version from github.com
+// versionFromGithub checks the version via github.com
 type versionFromGithub struct {
        Tag        string `json:"tag_name"`
        CommitHash string `json:"target_commitish"`
+       CreatedAt  string `json:"created_at"` // Refers to the build date, not 
the publishing date.
 }
 
 func (v *versionFromGithub) Version() string { return v.Tag }
@@ -176,3 +180,10 @@
 func (v *versionFromGithub) IsValid() bool {
        return strings.HasPrefix(v.Tag, "v") && len(v.CommitHash) >= 7
 }
+func (v *versionFromGithub) BuildDate() string {
+       t, err := time.Parse(time.RFC3339, v.CreatedAt)
+       if err != nil {
+               return "????-??-??"
+       }
+       return t.UTC().Format(time.DateOnly)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/context.go 
new/klog-7.2/klog/app/context.go
--- old/klog-7.1/klog/app/context.go    2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/app/context.go    2026-10-19 19:23:47.000000000 +0200
@@ -87,22 +87,6 @@
        Config() Config
 }
 
-// Meta holds miscellaneous information about the klog binary.
-type Meta struct {
-
-       // Specification contains the file format specification in full text.
-       Specification string
-
-       // License contains the license text.
-       License string
-
-       // Version contains the release version, e.g. `v2.7`.
-       Version string
-
-       // SrcHash contains the hash of the sources that the binary was built 
from.
-       SrcHash string
-}
-
 // NewContext creates a new Context object.
 func NewContext(klogFolder File, meta Meta, styler tf.Styler, cfg Config) 
Context {
        parserEngine := parser.NewSerialParser()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/main/decoder.go 
new/klog-7.2/klog/app/main/decoder.go
--- old/klog-7.1/klog/app/main/decoder.go       2026-02-22 11:46:19.000000000 
+0100
+++ new/klog-7.2/klog/app/main/decoder.go       2026-10-19 19:23:47.000000000 
+0200
@@ -148,7 +148,7 @@
                        return err
                }
                if value == "" {
-                       return errors.New("Please provide a valid record 
summary")
+                       return errors.New("Please provide a valid entry 
summary")
                }
                // Normalize potential double-escaping (from CLI)
                value = strings.ReplaceAll(value, "\\n", "\n")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/meta.go 
new/klog-7.2/klog/app/meta.go
--- old/klog-7.1/klog/app/meta.go       1970-01-01 01:00:00.000000000 +0100
+++ new/klog-7.2/klog/app/meta.go       2026-10-19 19:23:47.000000000 +0200
@@ -0,0 +1,59 @@
+package app
+
+import (
+       "regexp"
+       "strings"
+)
+
+// Meta holds miscellaneous information about the klog binary.
+type Meta struct {
+
+       // Specification contains the file format specification in full text.
+       Specification string
+
+       // License contains the license text.
+       License string
+
+       // Version contains the release version, e.g. `v2.7`.
+       Version string
+
+       // SrcHash contains the hash of the sources that the binary was built 
from.
+       SrcHash string
+
+       // BuildDate is the date at which the binary was built, e.g. 
`2026-10-19`.
+       BuildDate string
+}
+
+// NewMeta creates a meta info object from input strings. For invalid values, 
it
+// replaces them with ?-placeholders instead of failing. The validation is 
mostly
+// structural, not logical.
+func NewMeta(specification string, license string, version string, srcHash 
string, buildDate string) Meta {
+       return Meta{
+               Specification: specification,
+               License:       license,
+               Version: func() string {
+                       version = strings.TrimSpace(version)
+                       pattern := regexp.MustCompile(`^v\d+\.\d+([.-].+)?$`)
+                       if !pattern.MatchString(version) {
+                               return "v?.?"
+                       }
+                       return version
+               }(),
+               SrcHash: func() string {
+                       srcHash = strings.TrimSpace(srcHash)
+                       pattern := regexp.MustCompile(`^[a-fA-F0-9]{7,}$`)
+                       if !pattern.MatchString(srcHash) {
+                               return "???????"
+                       }
+                       return strings.ToLower(srcHash[:7])
+               }(),
+               BuildDate: func() string {
+                       buildDate = strings.TrimSpace(buildDate)
+                       pattern := 
regexp.MustCompile(`^\d{4}-\d{2}-\d{2}(T.*)?$`)
+                       if !pattern.MatchString(buildDate) {
+                               return "????-??-??"
+                       }
+                       return buildDate[:10]
+               }(),
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/app/meta_test.go 
new/klog-7.2/klog/app/meta_test.go
--- old/klog-7.1/klog/app/meta_test.go  1970-01-01 01:00:00.000000000 +0100
+++ new/klog-7.2/klog/app/meta_test.go  2026-10-19 19:23:47.000000000 +0200
@@ -0,0 +1,129 @@
+package app
+
+import (
+       "strings"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestCreateMeta(t *testing.T) {
+       m := NewMeta("Specification", "License", "v1.0", "1234abc", 
"2000-12-31")
+       assert.Equal(t, "Specification", m.Specification)
+       assert.Equal(t, "License", m.License)
+       assert.Equal(t, "v1.0", m.Version)
+       assert.Equal(t, "1234abc", m.SrcHash)
+       assert.Equal(t, "2000-12-31", m.BuildDate)
+}
+
+func TestCreateMetaTrimsWhiteSpace(t *testing.T) {
+       m := NewMeta("Specification", "License", "   v1.0   ", "  1234abc  ", " 
2000-12-31 ")
+       assert.Equal(t, "Specification", m.Specification)
+       assert.Equal(t, "License", m.License)
+       assert.Equal(t, "v1.0", m.Version)
+       assert.Equal(t, "1234abc", m.SrcHash)
+       assert.Equal(t, "2000-12-31", m.BuildDate)
+}
+
+func TestAcceptsValidVersion(t *testing.T) {
+       for _, in := range []string{
+               "v0.0",
+               "v000.000",
+               "v001.002",
+               "v1.1",
+               "v183.5",
+               "v2.5910",
+               "v28372.5910",
+               "v5.23-alpha",
+               "v5.23.42-alpha",
+               "v5.23.42-alpha_SOMETHING&$^%!*@&#",
+       } {
+               t.Run(in, func(t *testing.T) {
+                       m := NewMeta("Specification", "License", in, "1234abc", 
"2000-12-31")
+                       assert.Equal(t, in, m.Version)
+               })
+       }
+}
+
+func TestRejectsInvalidVersion(t *testing.T) {
+       for _, in := range []string{
+               "",
+               "foo",
+               "0.0",
+               "v1",
+               "v1.",
+               "v1.2.",
+               "v1.2-",
+               "v.12",
+               "v1.22alpha",
+       } {
+               t.Run(in, func(t *testing.T) {
+                       m := NewMeta("Specification", "License", in, "1234abc", 
"2000-12-31")
+                       assert.Equal(t, "v?.?", m.Version)
+               })
+       }
+}
+
+func TestAcceptsValidSrcHash(t *testing.T) {
+       for _, in := range []string{
+               "1a2b3c4",
+               "1A2B3C4",
+               "1590acf",
+               "1234567890abcdef",
+               "1234567890ABCDEF",
+               "7a4f7b6b0749adb164aa6ac67dee9ba6556f261e",
+       } {
+               t.Run(in, func(t *testing.T) {
+                       m := NewMeta("Specification", "License", "v1.2", in, 
"2000-12-31")
+                       assert.Equal(t, strings.ToLower(in[:7]), m.SrcHash)
+               })
+       }
+}
+
+func TestRejectsInvalidSrcHash(t *testing.T) {
+       for _, in := range []string{
+               "",
+               "0",
+               "123456",
+               "123456g",
+               "abcdefghijklmnopqrst",
+               "&^@!#@(*#&!*@&",
+       } {
+               t.Run(in, func(t *testing.T) {
+                       m := NewMeta("Specification", "License", "v1.2", in, 
"2000-12-31")
+                       assert.Equal(t, "???????", m.SrcHash)
+               })
+       }
+}
+
+func TestAcceptsValidBuildDate(t *testing.T) {
+       for _, in := range []string{
+               "2000-12-31",
+               "3617-05-15",
+               "9999-99-99",
+               "0001-01-01",
+               "2000-12-31T12:00:23Z",
+               "2000-12-31Tanything",
+       } {
+               t.Run(in, func(t *testing.T) {
+                       m := NewMeta("Specification", "License", "v1.2", 
"1234567", in)
+                       assert.Equal(t, in[:10], m.BuildDate)
+               })
+       }
+}
+
+func TestRejectsInvalidBuildDate(t *testing.T) {
+       for _, in := range []string{
+               "",
+               "153728182883",
+               "2000_12_31",
+               "2000 12 31",
+               "2000/12/31",
+               "abcd-ef-gh",
+       } {
+               t.Run(in, func(t *testing.T) {
+                       m := NewMeta("Specification", "License", "v1.2", 
"1234567", in)
+                       assert.Equal(t, "????-??-??", m.BuildDate)
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/klog-7.1/klog/parser/reconciling/append_to_summary.go 
new/klog-7.2/klog/parser/reconciling/append_to_summary.go
--- old/klog-7.1/klog/parser/reconciling/append_to_summary.go   1970-01-01 
01:00:00.000000000 +0100
+++ new/klog-7.2/klog/parser/reconciling/append_to_summary.go   2026-10-19 
19:23:47.000000000 +0200
@@ -0,0 +1,37 @@
+package reconciling
+
+import (
+       "errors"
+       "github.com/jotaen/klog/klog"
+)
+
+// AppendToOpenRange adds text to the summary of the open time range.
+func (r *Reconciler) AppendToOpenRange(additionalSummary klog.EntrySummary) 
error {
+       openRangeEntryIndex := r.findOpenRangeIndex()
+       if openRangeEntryIndex == -1 {
+               return errors.New("No open time range")
+       }
+       r.appendToSummary(openRangeEntryIndex, additionalSummary)
+       return nil
+}
+
+// AppendToNthEntry adds text to the summary of the nth entry. If nth is 
positive, it
+// counts from the start (beginning with 1); if negative, it counts from the 
end
+// (beginning with -1).
+func (r *Reconciler) AppendToNthEntry(nth int, additionalSummary 
klog.EntrySummary) error {
+       entriesCount := len(r.Record.Entries())
+       entryIndex := nth - 1
+       if nth < 0 {
+               entryIndex = entriesCount + nth
+       }
+       if nth == 0 || entryIndex < 0 || entryIndex >= entriesCount {
+               return errors.New("No such entry")
+       }
+       r.appendToSummary(entryIndex, additionalSummary)
+       return nil
+}
+
+func (r *Reconciler) appendToSummary(entryIndex int, additionalSummary 
klog.EntrySummary) {
+       entryValueLineIndex := r.lastLinePointer - 
countLines(r.Record.Entries()[entryIndex:])
+       r.concatenateSummary(entryIndex, entryValueLineIndex, additionalSummary)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/klog-7.1/klog/parser/reconciling/append_to_summary_test.go 
new/klog-7.2/klog/parser/reconciling/append_to_summary_test.go
--- old/klog-7.1/klog/parser/reconciling/append_to_summary_test.go      
1970-01-01 01:00:00.000000000 +0100
+++ new/klog-7.2/klog/parser/reconciling/append_to_summary_test.go      
2026-10-19 19:23:47.000000000 +0200
@@ -0,0 +1,101 @@
+package reconciling
+
+import (
+       "strconv"
+       "testing"
+
+       "github.com/jotaen/klog/klog"
+       "github.com/jotaen/klog/klog/parser"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestReconcilerAppendsToOpenRangeSummary(t *testing.T) {
+       original := `
+2018-01-01
+    8:00 - 9:00 Before
+    9:00 - ? First line
+        Second line
+    1h After
+`
+       rs, bs, _ := parser.NewSerialParser().Parse(original)
+       reconciler := NewReconcilerAtRecord(klog.Ɀ_Date_(2018, 1, 1))(rs, bs)
+       require.NotNil(t, reconciler)
+       err := reconciler.AppendToOpenRange(klog.Ɀ_EntrySummary_("", "Third 
line #tag"))
+       require.Nil(t, err)
+
+       result := assertResult(t, reconciler)
+       assert.Equal(t, `
+2018-01-01
+    8:00 - 9:00 Before
+    9:00 - ? First line
+        Second line
+        Third line #tag
+    1h After
+`, result.AllSerialised)
+}
+
+func TestReconcilerAppendToOpenRangeFailsWithoutOpenRange(t *testing.T) {
+       original := `
+2018-01-01
+    8:00 - 9:00
+`
+       rs, bs, _ := parser.NewSerialParser().Parse(original)
+       reconciler := NewReconcilerAtRecord(klog.Ɀ_Date_(2018, 1, 1))(rs, bs)
+       require.NotNil(t, reconciler)
+       err := reconciler.AppendToOpenRange(klog.Ɀ_EntrySummary_("", "Text"))
+       require.Error(t, err)
+}
+
+func TestReconcilerAppendsToNthEntrySummary(t *testing.T) {
+       original := `
+2018-01-01
+    8:00 - 9:00 First
+        continued
+    2h Second
+    1h Third
+`
+       rs, bs, _ := parser.NewSerialParser().Parse(original)
+       reconciler := NewReconcilerAtRecord(klog.Ɀ_Date_(2018, 1, 1))(rs, bs)
+       require.NotNil(t, reconciler)
+
+       t.Run("Add 1st line", func(t *testing.T) {
+               require.Nil(t, reconciler.AppendToNthEntry(2, 
klog.Ɀ_EntrySummary_("entry")))
+               result := assertResult(t, reconciler)
+               assert.Equal(t, `
+2018-01-01
+    8:00 - 9:00 First
+        continued
+    2h Second entry
+    1h Third
+`, result.AllSerialised)
+       })
+
+       t.Run("Add 2nd lines", func(t *testing.T) {
+               require.Nil(t, reconciler.AppendToNthEntry(-3, 
klog.Ɀ_EntrySummary_("", "more")))
+               result := assertResult(t, reconciler)
+               assert.Equal(t, `
+2018-01-01
+    8:00 - 9:00 First
+        continued
+        more
+    2h Second entry
+    1h Third
+`, result.AllSerialised)
+       })
+}
+
+func TestReconcilerAppendToNthEntryFailsForMissingEntry(t *testing.T) {
+       original := `
+2018-01-01
+    8:00 - 9:00
+`
+       rs, bs, _ := parser.NewSerialParser().Parse(original)
+       reconciler := NewReconcilerAtRecord(klog.Ɀ_Date_(2018, 1, 1))(rs, bs)
+       require.NotNil(t, reconciler)
+       for _, nth := range []int{0, 2, 20, -2, -20} {
+               t.Run("For entry nr: "+strconv.Itoa(nth), func(t *testing.T) {
+                       require.Error(t, reconciler.AppendToNthEntry(nth, 
klog.Ɀ_EntrySummary_("Text")))
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/service/record.go 
new/klog-7.2/klog/service/record.go
--- old/klog-7.1/klog/service/record.go 2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog/service/record.go 2026-10-19 19:23:47.000000000 +0200
@@ -2,18 +2,19 @@
 
 import (
        "errors"
-       "github.com/jotaen/klog/klog"
        gotime "time"
+
+       "github.com/jotaen/klog/klog"
 )
 
 // CloseOpenRanges closes open ranges at the time of `endTime`. Returns an 
error
 // if a range is not closeable at that point in time.
 // This method alters the provided records!
-// The bool return value indicates whether any open ranges have been closed.
-func CloseOpenRanges(endTime gotime.Time, rs ...klog.Record) (bool, error) {
+// The `map` return value indicates which open ranges have been closed.
+func CloseOpenRanges(endTime gotime.Time, rs ...klog.Record) 
(map[klog.Record]int, error) {
        thisDay := klog.NewDateFromGo(endTime)
        theDayBefore := thisDay.PlusDays(-1)
-       hasClosedAnyRange := false
+       closedEntries := map[klog.Record]int{}
        for _, r := range rs {
                if r.OpenRange() == nil {
                        continue
@@ -29,13 +30,28 @@
                        return nil, errors.New("Encountered uncloseable open 
range")
                }()
                if tErr != nil {
-                       return false, tErr
+                       return nil, tErr
                }
+               closedEntries[r] = openRangeIndex(r)
                eErr := r.EndOpenRange(end)
-               hasClosedAnyRange = true
                if eErr != nil {
-                       return false, errors.New("Encountered uncloseable open 
range")
+                       return nil, errors.New("Encountered uncloseable open 
range")
                }
        }
-       return hasClosedAnyRange, nil
+       return closedEntries, nil
+}
+
+func openRangeIndex(r klog.Record) int {
+       openEndedEntry := -1
+       for entryI, e := range r.Entries() {
+               klog.Unbox[any](&e,
+                       func(klog.Range) any { return nil },
+                       func(klog.Duration) any { return nil },
+                       func(klog.OpenRange) any {
+                               openEndedEntry = entryI
+                               return nil
+                       },
+               )
+       }
+       return openEndedEntry
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog/service/record_test.go 
new/klog-7.2/klog/service/record_test.go
--- old/klog-7.1/klog/service/record_test.go    2026-02-22 11:46:19.000000000 
+0100
+++ new/klog-7.2/klog/service/record_test.go    2026-10-19 19:23:47.000000000 
+0200
@@ -1,11 +1,12 @@
 package service
 
 import (
+       "testing"
+       gotime "time"
+
        "github.com/jotaen/klog/klog"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
-       "testing"
-       gotime "time"
 )
 
 func TestDoesNotTouchRecordsIfNoOpenRange(t *testing.T) {
@@ -13,9 +14,9 @@
        r.AddDuration(klog.NewDuration(1, 0), nil)
        r.AddRange(klog.Ɀ_Range_(klog.Ɀ_Time_(1, 0), klog.Ɀ_Time_(2, 0)), nil)
 
-       hasClosedAnyRange, err := CloseOpenRanges(gotime.Now(), r)
+       closedEntries, err := CloseOpenRanges(gotime.Now(), r)
        require.Nil(t, err)
-       assert.False(t, hasClosedAnyRange)
+       assert.Len(t, closedEntries, 0)
        assert.Equal(t, klog.NewDuration(2, 0), Total(r))
 }
 
@@ -26,9 +27,10 @@
        r.Start(klog.NewOpenRange(klog.Ɀ_Time_(3, 0)), nil)
 
        endTime, _ := gotime.Parse("2006-01-02T15:04:05-0700", 
"2020-01-01T05:30:00-0000")
-       hasClosedAnyRange, err := CloseOpenRanges(endTime, r)
+       closedEntries, err := CloseOpenRanges(endTime, r)
        require.Nil(t, err)
-       assert.True(t, hasClosedAnyRange)
+       require.Len(t, closedEntries, 1)
+       assert.Equal(t, 2, closedEntries[r])
        assert.Equal(t, klog.NewDuration(2+2, 30), Total(r))
 }
 
@@ -39,9 +41,10 @@
        r.Start(klog.NewOpenRange(klog.Ɀ_Time_(3, 0)), nil)
 
        endTime, _ := gotime.Parse("2006-01-02T15:04:05-0700", 
"2020-01-02T05:30:00-0000")
-       hasClosedAnyRange, err := CloseOpenRanges(endTime, r)
+       closedEntries, err := CloseOpenRanges(endTime, r)
        require.Nil(t, err)
-       assert.True(t, hasClosedAnyRange)
+       require.Len(t, closedEntries, 1)
+       assert.Equal(t, 2, closedEntries[r])
        assert.Equal(t, klog.NewDuration(2+24+2, 30), Total(r))
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/klog.go new/klog-7.2/klog.go
--- old/klog-7.1/klog.go        2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/klog.go        2026-10-19 19:23:47.000000000 +0200
@@ -17,13 +17,29 @@
 //go:embed LICENSE.txt
 var license string
 
-var BinaryVersion string   // Set via build flag
-var BinaryBuildHash string // Set via build flag
+// BinaryVersion is the version identifier that is embedded in the binary.
+// It should be provided via a compile/build flag. The value must start with a
+// `v`, followed by 2 dot-separated integers; e.g., `v2.12`. Optionally, there
+// can be a dot- or hyphen-separated suffix (containing arbitrary characters),
+// e.g., `v12.9-alpha` or `v8.1.0-alpha_2*`. If the build flag is absent, a 
value
+// of `v?.?` is used.
+var BinaryVersion string
+
+// BinaryBuildHash is a computed hash of the underlying sources of the build.
+// It should be provided via a compile/build flag. This is usually the git 
commit
+// hash, e.g. `7ab819c`. The value must consist of at least 7 hexadecimal 
digits.
+// Longer values are truncated; the value will also be normalised to 
lower-case. If
+// the build flag is absent, a value of `???????` is used.
+var BinaryBuildHash string
+
+// BinaryBuildDate denotes the date that the build refers to. It should be 
provided
+// via a compile/build flag. This should be the date of the latest commit, or
+// alternatively the date at which the build occurred. It must be a valid 
calendar
+// date, formatted `YYYY-MM-DD`; e.g. `2026-10-26`. If the build flag is 
absent, a
+// value of `????-??-??` is used.
+var BinaryBuildDate string
 
 func main() {
-       if len(BinaryBuildHash) > 7 {
-               BinaryBuildHash = BinaryBuildHash[:7]
-       }
 
        klogFolder := func() app.File {
                f, err := determineKlogConfigFolder()
@@ -49,12 +65,12 @@
                return c
        }()
 
-       code, err := klog.Run(klogFolder, app.Meta{
-               Specification: specification,
-               License:       license,
-               Version:       BinaryVersion,
-               SrcHash:       BinaryBuildHash,
-       }, config, os.Args[1:])
+       code, err := klog.Run(
+               klogFolder,
+               app.NewMeta(specification, license, BinaryVersion, 
BinaryBuildHash, BinaryBuildDate),
+               config,
+               os.Args[1:],
+       )
        if err != nil {
                fail(err, code)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.1/run.sh new/klog-7.2/run.sh
--- old/klog-7.1/run.sh 2026-02-22 11:46:19.000000000 +0100
+++ new/klog-7.2/run.sh 2026-10-19 19:23:47.000000000 +0200
@@ -1,4 +1,5 @@
 #!/bin/bash
+# Use this script with https://run.jotaen.net
 
 # Install all dependencies
 run::install() {
@@ -7,15 +8,17 @@
 }
 
 # Compile to ./out/klog
-# Takes two positional arguments:
-# - The version (e.g.: v1.2)
-# - The build hash (7 chars hex)
+# Takes three positional arguments (see `klog.go` file):
+# - Version string (e.g.: v1.2)
+# - Build hash (7 chars hex)
+# - Build date (formatted YYYY-MM-DD)
 run::build() {
        go build \
          -ldflags "\
            -s -w \
-           -X 'main.BinaryVersion=${1:-v?.?}' \
-           -X 'main.BinaryBuildHash=${2:-???????}' \
+           -X 'main.BinaryVersion=$1' \
+           -X 'main.BinaryBuildHash=$2' \
+           -X 'main.BinaryBuildDate=$3' \
            " \
          -o ./out/klog \
          klog.go

++++++ klog.obsinfo ++++++
--- /var/tmp/diff_new_pack.eunrgv/_old  2026-10-20 12:44:06.091000237 +0200
+++ /var/tmp/diff_new_pack.eunrgv/_new  2026-10-20 12:44:06.094000362 +0200
@@ -1,5 +1,5 @@
 name: klog
-version: 7.1
-mtime: 1771757179
-commit: 7a4f7b6b0749adb164aa6ac67dee9ba6556f261e
+version: 7.2
+mtime: 1792430627
+commit: 66d62de2091364685ffd5226bf35f11104f9246b
 

++++++ vendor.tar.gz ++++++
++++ 31270 lines of diff (skipped)

Reply via email to