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-02-25 21:12:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/klog (Old)
 and      /work/SRC/openSUSE:Factory/.klog.new.1977 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "klog"

Wed Feb 25 21:12:09 2026 rev:6 rq:1335043 version:7.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/klog/klog.changes        2026-02-09 
15:35:50.453893652 +0100
+++ /work/SRC/openSUSE:Factory/.klog.new.1977/klog.changes      2026-02-25 
21:21:31.569224800 +0100
@@ -1,0 +2,17 @@
+Wed Feb 25 09:31:45 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 7.1:
+  * [ FEATURE ] Include warnings in JSON output of klog json
+    subcommand.
+  * [ FEATURE ] When using klog report --fill and combining this
+    with a periodic date filter such as --this-week, --last-month
+    or --period 2021-05, fill up the entire queried period, rather
+    than just the gaps in between.
+  * [ FEATURE ] Display warning when using --diff together with
+    entry-level filtering, as that likely yields nonsensical
+    results.
+  * [ FIX ] Revise and define behaviour when applying filters to
+    empty records (i.e., records that don’t contain any time
+    entries).
+
+-------------------------------------------------------------------

Old:
----
  klog-7.0.obscpio

New:
----
  klog-7.1.obscpio

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

Other differences:
------------------
++++++ klog.spec ++++++
--- /var/tmp/diff_new_pack.1cSCwN/_old  2026-02-25 21:21:32.081245897 +0100
+++ /var/tmp/diff_new_pack.1cSCwN/_new  2026-02-25 21:21:32.085246062 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           klog
-Version:        7.0
+Version:        7.1
 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.25
+BuildRequires:  golang(API) >= 1.26
 
 %description
 klog is a plain-text file format and a command line tool for time tracking.

++++++ _service ++++++
--- /var/tmp/diff_new_pack.1cSCwN/_old  2026-02-25 21:21:32.121247545 +0100
+++ /var/tmp/diff_new_pack.1cSCwN/_new  2026-02-25 21:21:32.125247710 +0100
@@ -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.0</param>
+    <param name="revision">v7.1</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.0</param>
+    <param name="basename">klog-7.1</param>
   </service>
   <!-- services below are running at buildtime -->
   <service name="tar" mode="buildtime">

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.1cSCwN/_old  2026-02-25 21:21:32.145248534 +0100
+++ /var/tmp/diff_new_pack.1cSCwN/_new  2026-02-25 21:21:32.149248699 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/jotaen/klog</param>
-              <param 
name="changesrevision">76fdc21899f3547d28f6e80e4179959a92bcd999</param></service></servicedata>
+              <param 
name="changesrevision">7a4f7b6b0749adb164aa6ac67dee9ba6556f261e</param></service></servicedata>
 (No newline at EOF)
 

++++++ klog-7.0.obscpio -> klog-7.1.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/CHANGELOG.md new/klog-7.1/CHANGELOG.md
--- old/klog-7.0/CHANGELOG.md   2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/CHANGELOG.md   2026-02-22 11:46:19.000000000 +0100
@@ -1,6 +1,16 @@
 # Changelog
 **Summary of changes of the command line tool**
 
+## 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
+  date filter such as `--this-week`, `--last-month` or `--period 2021-05`, fill
+  up the entire queried period, rather than just the gaps in between.
+- **[ FEATURE ]** Display warning when using `--diff` together with entry-level
+  filtering, as that likely yields nonsensical results.
+- **[ FIX ]** Revise and define behaviour when applying filters to empty 
records
+  (i.e., records that don’t contain any time entries). 
+
 ## v7.0 (2026-02-06)
 - **[ META ]** klog is 5 years old today – happy birthday! 🥳
 - **[ FEATURE ]** Introduce `--filter` flag for applying generic and more 
complex
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/README.md new/klog-7.1/README.md
--- old/klog-7.0/README.md      2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/README.md      2026-02-22 11:46:19.000000000 +0100
@@ -23,8 +23,6 @@
 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).
 
-If you’d like to contribute code, please discuss your intended change 
beforehand. Please refrain from submitting pull requests proactively.
-
 ## About
 
 klog was created by [Jan Heuermann](https://www.jotaen.net).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/go.mod new/klog-7.1/go.mod
--- old/klog-7.0/go.mod 2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/go.mod 2026-02-22 11:46:19.000000000 +0100
@@ -1,12 +1,12 @@
 module github.com/jotaen/klog
 
-go 1.25
+go 1.26
 
 require (
        cloud.google.com/go v0.123.0
-       github.com/alecthomas/kong v1.13.0
+       github.com/alecthomas/kong v1.14.0
        github.com/jotaen/genie v0.0.3
-       github.com/jotaen/kong-completion v0.0.11
+       github.com/jotaen/kong-completion v0.0.12
        github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
        github.com/posener/complete v1.2.3
        github.com/stretchr/testify v1.11.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/go.sum new/klog-7.1/go.sum
--- old/klog-7.0/go.sum 2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/go.sum 2026-02-22 11:46:19.000000000 +0100
@@ -2,8 +2,8 @@
 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.13.0 
h1:5e/7XC3ugvhP1DQBmTS+WuHtCbcv44hsohMgcvVxSrA=
-github.com/alecthomas/kong v1.13.0/go.mod 
h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
+github.com/alecthomas/kong v1.14.0 
h1:gFgEUZWu2ZmZ+UhyZ1bDhuutbKN1nTtJTwh19Wsn21s=
+github.com/alecthomas/kong v1.14.0/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=
@@ -21,8 +21,8 @@
 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.11 
h1:ZRyQt+IwjcAObbiyxJZ3YR7r/o/f6HYidTK1+7YNtnE=
-github.com/jotaen/kong-completion v0.0.11/go.mod 
h1:dyIG20e3qq128SUBtF8jzI7YtkfzjWMlgbqkAJd6xHQ=
+github.com/jotaen/kong-completion v0.0.12 
h1:a9jmSaWgkdAUMQT583UxLIJrO9tfdSmYqcIxrBByjPc=
+github.com/jotaen/kong-completion v0.0.12/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=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/args/filter.go 
new/klog-7.1/klog/app/cli/args/filter.go
--- old/klog-7.0/klog/app/cli/args/filter.go    2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/app/cli/args/filter.go    2026-02-22 11:46:19.000000000 
+0100
@@ -11,31 +11,34 @@
 
 type FilterArgs struct {
        // Date-related filters:
-       Date   klog.Date     `name:"date" placeholder:"DATE" group:"Filter" 
help:"Entries at this date. DATE has to be in format YYYY-MM-DD or YYYY/MM/DD. 
E.g., '2024-01-31' or '2024/01/31'."`
-       Since  klog.Date     `name:"since" placeholder:"DATE" group:"Filter" 
help:"Entries since this date (inclusive)."`
-       Until  klog.Date     `name:"until" placeholder:"DATE" group:"Filter" 
help:"Entries until this date (inclusive)."`
-       Period period.Period `name:"period" placeholder:"PERIOD" group:"Filter" 
help:"Entries within a calendar period. PERIOD has to be in format YYYY, 
YYYY-MM, YYYY-Www or YYYY-Qq. E.g., '2024', '2024-04', '2022-W21' or 
'2024-Q1'."`
+       Date   klog.Date     `name:"date" placeholder:"DATE" group:"Filter 
Flags:" help:"Records at this date. DATE has to be in format YYYY-MM-DD or 
YYYY/MM/DD. E.g., '2024-01-31' or '2024/01/31'."`
+       Since  klog.Date     `name:"since" placeholder:"DATE" group:"Filter 
Flags:" help:"Records since this date (inclusive)."`
+       Until  klog.Date     `name:"until" placeholder:"DATE" group:"Filter 
Flags:" help:"Records until this date (inclusive)."`
+       Period period.Period `name:"period" placeholder:"PERIOD" group:"Filter 
Flags:" help:"Records within a calendar period. PERIOD has to be in format 
YYYY, YYYY-MM, YYYY-Www or YYYY-Qq. E.g., '2024', '2024-04', '2022-W21' or 
'2024-Q1'."`
 
        // Filter shortcuts:
        // The two `XXX` ones are dummy entries just for the help output, they 
also aren’t available
        // for tab completion. The other ones are not shown in the help output 
(because that would be
        // too verbose then), but they are still available for tab completion.
-       Today       bool `name:"today" group:"Filter" help:"Records at today’s 
date."`
-       Yesterday   bool `name:"yesterday" group:"Filter" help:"Records at 
yesterday’s date."`
-       ThisXXX     bool `name:"this-***" group:"Filter" help:"Records of this 
week/month/quarter/year, e.g. '--this-week' or '--this-quarter'." 
completion-enabled:"false"`
-       LastXXX     bool `name:"last-***" group:"Filter" help:"Records of last 
week/month/quarter/year, e.g. '--last-month' or '--last-year'." 
completion-enabled:"false"`
-       ThisWeek    bool `hidden:"" name:"this-week" group:"Filter" 
completion-enabled:"true"`
-       LastWeek    bool `hidden:"" name:"last-week" group:"Filter" 
completion-enabled:"true"`
-       ThisMonth   bool `hidden:"" name:"this-month" group:"Filter" 
completion-enabled:"true"`
-       LastMonth   bool `hidden:"" name:"last-month" group:"Filter" 
completion-enabled:"true"`
-       ThisQuarter bool `hidden:"" name:"this-quarter" group:"Filter" 
completion-enabled:"true"`
-       LastQuarter bool `hidden:"" name:"last-quarter" group:"Filter" 
completion-enabled:"true"`
-       ThisYear    bool `hidden:"" name:"this-year" group:"Filter" 
completion-enabled:"true"`
-       LastYear    bool `hidden:"" name:"last-year" group:"Filter" 
completion-enabled:"true"`
+       Today       bool `name:"today" group:"Filter Flags:" help:"Records at 
today’s date."`
+       Yesterday   bool `name:"yesterday" group:"Filter Flags:" help:"Records 
at yesterday’s date."`
+       ThisXXX     bool `name:"this-***" group:"Filter Flags:" help:"Records 
of this week/month/quarter/year, e.g. '--this-week' or '--this-quarter'." 
completion-enabled:"false"`
+       LastXXX     bool `name:"last-***" group:"Filter Flags:" help:"Records 
of last week/month/quarter/year, e.g. '--last-month' or '--last-year'." 
completion-enabled:"false"`
+       ThisWeek    bool `hidden:"" name:"this-week" group:"Filter Flags:" 
completion-enabled:"true"`
+       LastWeek    bool `hidden:"" name:"last-week" group:"Filter Flags:" 
completion-enabled:"true"`
+       ThisMonth   bool `hidden:"" name:"this-month" group:"Filter Flags:" 
completion-enabled:"true"`
+       LastMonth   bool `hidden:"" name:"last-month" group:"Filter Flags:" 
completion-enabled:"true"`
+       ThisQuarter bool `hidden:"" name:"this-quarter" group:"Filter Flags:" 
completion-enabled:"true"`
+       LastQuarter bool `hidden:"" name:"last-quarter" group:"Filter Flags:" 
completion-enabled:"true"`
+       ThisYear    bool `hidden:"" name:"this-year" group:"Filter Flags:" 
completion-enabled:"true"`
+       LastYear    bool `hidden:"" name:"last-year" group:"Filter Flags:" 
completion-enabled:"true"`
 
        // General filters:
-       Tags   []klog.Tag `name:"tag" placeholder:"TAG" group:"Filter" 
help:"Entries that match these tags (either in the record summary or the entry 
summary). You can omit the leading '#'."`
-       Filter string     `name:"filter" placeholder:"EXPR" group:"Filter" 
help:"Entries that match this filter expression. Run 'klog info --filtering' to 
learn how expressions works."`
+       Tags   []klog.Tag `name:"tag" placeholder:"TAG" group:"Filter Flags:" 
help:"Records or entries that match these tags (either in the record summary or 
the entry summary). You can omit the leading '#'."`
+       Filter string     `name:"filter" placeholder:"EXPR" group:"Filter 
Flags:" help:"Records or entries that match this filter expression. Run 'klog 
info --filtering' to learn how expressions works."`
+
+       hasPartialRecordsWithShouldTotal bool          // Field only for 
internal use
+       singleShortHandFilter            period.Period // Field only for 
internal use
 }
 
 func (args *FilterArgs) ApplyFilter(now gotime.Time, rs []klog.Record) 
([]klog.Record, app.Error) {
@@ -83,6 +86,9 @@
                }
                return res
        }()
+       if len(dateRanges) == 1 {
+               args.singleShortHandFilter = dateRanges[0]
+       }
        for _, d := range dateRanges {
                predicates = append(predicates, filter.IsInDateRange{
                        From: d.Since(),
@@ -125,7 +131,16 @@
 
        // Apply filters, if applicable:
        if len(predicates) > 0 {
-               rs = filter.Filter(filter.And{Predicates: predicates}, rs)
+               hprws := false
+               rs, hprws = filter.Filter(filter.And{Predicates: predicates}, 
rs)
+               args.hasPartialRecordsWithShouldTotal = hprws
+
        }
        return rs, nil
 }
+
+// SinglePeriodRequested returns the corresponding period if a single 
short-hand
+// periodic filter (such as --this-month, --period) was used.
+func (args *FilterArgs) SinglePeriodRequested() period.Period {
+       return args.singleShortHandFilter
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/args/misc.go 
new/klog-7.1/klog/app/cli/args/misc.go
--- old/klog-7.0/klog/app/cli/args/misc.go      2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/app/cli/args/misc.go      2026-02-22 11:46:19.000000000 
+0100
@@ -13,6 +13,16 @@
        Diff bool `name:"diff" short:"d" help:"Show difference between actual 
and should-total time."`
 }
 
+// GetWarning returns a warning if the user applied entry-level filtering 
(partial
+// records) *and* requested to compute the should-total diff, as that may yield
+// nonsensical results.
+func (args *DiffArgs) GetWarning(filterArgs FilterArgs) service.UsageWarning {
+       if args.Diff && filterArgs.hasPartialRecordsWithShouldTotal {
+               return service.EntryFilteredDiffWarning
+       }
+       return service.UsageWarning{}
+}
+
 type NoStyleArgs struct {
        NoStyle bool `name:"no-style" help:"Do not style or colour the values."`
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/args/now.go 
new/klog-7.1/klog/app/cli/args/now.go
--- old/klog-7.0/klog/app/cli/args/now.go       2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/app/cli/args/now.go       2026-02-22 11:46:19.000000000 
+0100
@@ -34,9 +34,11 @@
        return args.hadOpenRange
 }
 
-func (args *NowArgs) GetNowWarnings() []string {
+// 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 {
-               return []string{"You specified --now, but there was no 
open-ended time range."}
+               return service.PointlessNowWarning
        }
-       return nil
+       return service.UsageWarning{}
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/args/warn.go 
new/klog-7.1/klog/app/cli/args/warn.go
--- old/klog-7.0/klog/app/cli/args/warn.go      2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/app/cli/args/warn.go      2026-02-22 11:46:19.000000000 
+0100
@@ -11,16 +11,24 @@
        NoWarn bool `name:"no-warn" help:"Suppress warnings about potential 
mistakes or logical errors."`
 }
 
-func (args *WarnArgs) PrintWarnings(ctx app.Context, records []klog.Record, 
additionalWarnings []string) {
+func (args *WarnArgs) PrintWarnings(ctx app.Context, records []klog.Record, 
additionalWarnings []service.UsageWarning) {
        styler, _ := ctx.Serialise()
-       if args.NoWarn {
-               return
+       warnings := args.GatherWarnings(ctx, records, additionalWarnings)
+       for _, w := range warnings {
+               ctx.Print(prettify.PrettifyWarning(w, styler))
        }
-       for _, msg := range additionalWarnings {
-               ctx.Print(prettify.PrettifyGeneralWarning(msg, styler))
+}
+
+func (args *WarnArgs) GatherWarnings(ctx app.Context, records []klog.Record, 
additionalWarnings []service.UsageWarning) []string {
+       if args.NoWarn {
+               return nil
        }
        disabledCheckers := 
ctx.Config().NoWarnings.UnwrapOr(service.NewDisabledCheckers())
-       service.CheckForWarnings(func(w service.Warning) {
-               ctx.Print(prettify.PrettifyWarning(w, styler))
-       }, ctx.Now(), records, disabledCheckers)
+       warnings := service.CheckForWarnings(ctx.Now(), records, 
disabledCheckers)
+       for _, warn := range additionalWarnings {
+               if warn != (service.UsageWarning{}) && 
!disabledCheckers[warn.Name] {
+                       warnings = append(warnings, warn.Message)
+               }
+       }
+       return warnings
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/info.go 
new/klog-7.1/klog/app/cli/info.go
--- old/klog-7.0/klog/app/cli/info.go   2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/info.go   2026-02-22 11:46:19.000000000 +0100
@@ -25,7 +25,9 @@
 
     klog total --filter='2025-04 && #work' mytimes.klg
 
-This would evaluate all entries in April 2025 that match the tag #work. Wrap 
the filter expression in single quotes to avoid undesired shell word splitting 
or substitution. Filter expressions consist of operands for matching the data 
that shall be included in the filter result. Operands can be combined via 
logical operators and grouped via parentheses.
+This would evaluate all records and entries in April 2025 that match the tag 
#work. Wrap the filter expression in single quotes to avoid undesired shell 
word splitting or substitution. Filter expressions consist of operands for 
matching the data that shall be included in the filter result. Operands can be 
combined via logical operators and grouped via parentheses.
+
+Filters can match at record-level and/or at entry-level. It only keeps the 
data that satisfies the filter condition. For entry-level filters, this means 
that all non-matching entries are stripped from the record.
 
 Examples:
     2025-04-20 || 2020-04-21
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/json.go 
new/klog-7.1/klog/app/cli/json.go
--- old/klog-7.0/klog/app/cli/json.go   2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/json.go   2026-02-22 11:46:19.000000000 +0100
@@ -4,6 +4,7 @@
        "github.com/jotaen/klog/klog/app"
        "github.com/jotaen/klog/klog/app/cli/args"
        "github.com/jotaen/klog/klog/parser/json"
+       "github.com/jotaen/klog/klog/service"
 )
 
 type Json struct {
@@ -11,16 +12,21 @@
        args.NowArgs
        args.FilterArgs
        args.SortArgs
+       args.WarnArgs
        args.InputFilesArgs
 }
 
 func (opt *Json) Help() string {
        return `
-The output structure is a JSON object which contains two properties at the top 
level: 'records' and 'errors'.
-If the file is valid, 'records' is an array containing a JSON object for each 
record, and 'errors' is 'null'.
-If the file has syntax errors, 'records' is 'null', and 'errors' contains an 
array of error objects.
+The output structure is a JSON object which contains three properties at the 
top level: 'records', 'warnings' and 'errors':
 
-The structure of the 'record' and 'error' objects is always uniform and should 
be self-explanatory.
+- If the file is valid, 'records' is an array containing a JSON object for 
each record, and 'errors' is 'null'.
+
+- If the file is valid, but contains potential problems, 'warnings' is an 
array of strings with the respective messages.
+
+- If the file has syntax errors, 'records' and 'warnings' are 'null', and 
'errors' contains an array of error objects.
+
+The 'record', 'warnings' and 'error' data structures are always uniform and 
should be self-explanatory.
 You can best explore it by running the command with the --pretty flag.
 `
 }
@@ -30,7 +36,7 @@
        if err != nil {
                parserErrs, isParserErr := err.(app.ParserErrors)
                if isParserErr {
-                       ctx.Print(json.ToJson(nil, parserErrs.All(), 
opt.Pretty) + "\n")
+                       ctx.Print(json.ToJson(nil, parserErrs.All(), nil, 
opt.Pretty) + "\n")
                        return nil
                }
                return err
@@ -45,6 +51,7 @@
                return fErr
        }
        records = opt.ApplySort(records)
-       ctx.Print(json.ToJson(records, nil, opt.Pretty) + "\n")
+       warnings := opt.GatherWarnings(ctx, records, 
[]service.UsageWarning{opt.GetWarning()})
+       ctx.Print(json.ToJson(records, nil, warnings, opt.Pretty) + "\n")
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/prettify/prettifier.go 
new/klog-7.1/klog/app/cli/prettify/prettifier.go
--- old/klog-7.0/klog/app/cli/prettify/prettifier.go    2026-02-06 
22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/prettify/prettifier.go    2026-02-22 
11:46:19.000000000 +0100
@@ -6,7 +6,6 @@
        "strings"
 
        "github.com/jotaen/klog/klog/app"
-       "github.com/jotaen/klog/klog/service"
        "github.com/jotaen/klog/klog/service/filter"
        tf "github.com/jotaen/klog/lib/terminalformat"
 )
@@ -81,13 +80,8 @@
        )
 }
 
-// PrettifyWarning formats a warning about a record.
-func PrettifyWarning(w service.Warning, styler tf.Styler) string {
-       return PrettifyGeneralWarning(w.Date().ToString()+": "+w.Warning(), 
styler)
-}
-
-// PrettifyGeneralWarning formats a general warning message.
-func PrettifyGeneralWarning(message string, styler tf.Styler) string {
+// PrettifyWarning formats a warning message.
+func PrettifyWarning(message string, styler tf.Styler) string {
        result := ""
        result += styler.Props(tf.StyleProps{Background: tf.YELLOW, Color: 
tf.YELLOW}).Format("[")
        result += styler.Props(tf.StyleProps{Background: tf.YELLOW, Color: 
tf.TEXT_INVERSE}).Format("WARNING")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/print.go 
new/klog-7.1/klog/app/cli/print.go
--- old/klog-7.0/klog/app/cli/print.go  2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/print.go  2026-02-22 11:46:19.000000000 +0100
@@ -23,9 +23,8 @@
 func (opt *Print) Help() string {
        return `
 Outputs data on the terminal, by default with syntax-highlighting turned on.
-Note that the output doesn’t resemble the file byte by byte, but the command 
may apply some minor clean-ups of the formatting.
+Note that the output doesn’t resemble the file verbatim, but it may apply some 
minor formatting.
 
-If run with filter flags, it only outputs those entries that match the filter 
clauses.
 You can optionally also sort the records, or print out the total times for 
each record and entry.
 `
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/report.go 
new/klog-7.1/klog/app/cli/report.go
--- old/klog-7.0/klog/app/cli/report.go 2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/report.go 2026-02-22 11:46:19.000000000 +0100
@@ -65,7 +65,12 @@
        aggregator := opt.aggregator()
        recordGroups, dates := groupByDate(aggregator.DateHash, records)
        if opt.Fill {
-               dates = allDatesRange(records[0].Date(), 
records[len(records)-1].Date())
+               singlePeriod := opt.FilterArgs.SinglePeriodRequested()
+               if singlePeriod != nil {
+                       dates = allDatesRange(singlePeriod.Since(), 
singlePeriod.Until())
+               } else {
+                       dates = allDatesRange(records[0].Date(), 
records[len(records)-1].Date())
+               }
        }
 
        // Table setup
@@ -145,7 +150,7 @@
        }
 
        table.Collect(ctx.Print)
-       opt.WarnArgs.PrintWarnings(ctx, records, opt.GetNowWarnings())
+       opt.WarnArgs.PrintWarnings(ctx, records, 
[]service.UsageWarning{opt.NowArgs.GetWarning(), 
opt.DiffArgs.GetWarning(opt.FilterArgs)})
        return nil
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/report_test.go 
new/klog-7.1/klog/app/cli/report_test.go
--- old/klog-7.0/klog/app/cli/report_test.go    2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/app/cli/report_test.go    2026-02-22 11:46:19.000000000 
+0100
@@ -282,6 +282,29 @@
 `, state.printBuffer)
 }
 
+func TestPeriodicalReportWithFill(t *testing.T) {
+       state, err := NewTestingContext()._SetNow(2018, 8, 24, 0, 
0)._SetRecords(`
+2018-04-02 (8h!)
+       8h
+
+2018-08-10 (5h30m!)
+       2h
+
+2018-08-23 (2h!)
+       5h20m
+`)._Run((&Report{AggregateBy: "quarter", DiffArgs: args.DiffArgs{Diff: true}, 
Fill: true, FilterArgs: args.FilterArgs{ThisYear: true}}).Run)
+       require.Nil(t, err)
+       assert.Equal(t, `
+           Total    Should     Diff
+2018 Q1                            
+     Q2       8h       8h!       0m
+     Q3    7h20m    7h30m!     -10m
+     Q4                            
+        ======== ========= ========
+          15h20m   15h30m!     -10m
+`, state.printBuffer)
+}
+
 func TestReportWithChart(t *testing.T) {
        t.Run("Daily (default) aggregation", func(t *testing.T) {
                state, err := NewTestingContext()._SetRecords(`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/tags.go 
new/klog-7.1/klog/app/cli/tags.go
--- old/klog-7.0/klog/app/cli/tags.go   2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/tags.go   2026-02-22 11:46:19.000000000 +0100
@@ -94,6 +94,6 @@
                }
        }
        table.Collect(ctx.Print)
-       opt.WarnArgs.PrintWarnings(ctx, records, opt.GetNowWarnings())
+       opt.WarnArgs.PrintWarnings(ctx, records, 
[]service.UsageWarning{opt.NowArgs.GetWarning()})
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/today.go 
new/klog-7.1/klog/app/cli/today.go
--- old/klog-7.0/klog/app/cli/today.go  2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/today.go  2026-02-22 11:46:19.000000000 +0100
@@ -191,7 +191,7 @@
                }
        }
        table.Collect(ctx.Print)
-       opt.WarnArgs.PrintWarnings(ctx, records, opt.GetNowWarnings())
+       opt.WarnArgs.PrintWarnings(ctx, records, 
[]service.UsageWarning{opt.NowArgs.GetWarning()})
        return nil
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/cli/total.go 
new/klog-7.1/klog/app/cli/total.go
--- old/klog-7.0/klog/app/cli/total.go  2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/cli/total.go  2026-02-22 11:46:19.000000000 +0100
@@ -59,6 +59,6 @@
                return "s"
        }()))
 
-       opt.WarnArgs.PrintWarnings(ctx, records, opt.GetNowWarnings())
+       opt.WarnArgs.PrintWarnings(ctx, records, 
[]service.UsageWarning{opt.NowArgs.GetWarning(), 
opt.DiffArgs.GetWarning(opt.FilterArgs)})
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/app/config.go 
new/klog-7.1/klog/app/config.go
--- old/klog-7.0/klog/app/config.go     2026-02-06 22:46:44.000000000 +0100
+++ new/klog-7.1/klog/app/config.go     2026-02-22 11:46:19.000000000 +0100
@@ -239,7 +239,14 @@
                Name: "no_warnings",
                Help: Help{
                        Summary: "Whether klog should suppress certain warnings 
when processing files.",
-                       Value:   "The config property must be one (or several 
comma-separated) of: `UNCLOSED_OPEN_RANGE` (for unclosed open ranges in past 
records), `FUTURE_ENTRIES` (for records/entries in the future), 
`OVERLAPPING_RANGES` (for time ranges that overlap), `MORE_THAN_24H` (if there 
is a record with more than 24h total). Multiple values must be separated by a 
comma, e.g.: `UNCLOSED_OPEN_RANGE, MORE_THAN_24H`.",
+                       Value: "The config property must be one or several 
(comma-separated) of: " +
+                               "`UNCLOSED_OPEN_RANGE` (for unclosed open 
ranges in past records), " +
+                               "`FUTURE_ENTRIES` (for records/entries in the 
future), " +
+                               "`OVERLAPPING_RANGES` (for time ranges that 
overlap), " +
+                               "`MORE_THAN_24H` (if there is a record with 
more than 24h total), " +
+                               "`POINTLESS_NOW` (when using --now without any 
open ranges), " +
+                               "`ENTRY_FILTERED_DIFFING` (when combining 
--diff and entry-level filtering). " +
+                               "Multiple values must be separated by a comma, 
e.g.: `UNCLOSED_OPEN_RANGE, MORE_THAN_24H`.",
                        Default: "If absent/empty, klog prints all available 
warnings.",
                },
                read: func(value string, config *Config) error {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/parser/json/serialiser.go 
new/klog-7.1/klog/parser/json/serialiser.go
--- old/klog-7.0/klog/parser/json/serialiser.go 2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/parser/json/serialiser.go 2026-02-22 11:46:19.000000000 
+0100
@@ -6,27 +6,30 @@
 import (
        "bytes"
        "encoding/json"
+       "sort"
+       "strings"
+
        "github.com/jotaen/klog/klog"
        "github.com/jotaen/klog/klog/parser"
        "github.com/jotaen/klog/klog/parser/txt"
        "github.com/jotaen/klog/klog/service"
-       "sort"
-       "strings"
 )
 
 // ToJson serialises records into their JSON representation. The output
 // structure is RecordView at the top level.
-func ToJson(rs []klog.Record, errs []txt.Error, prettyPrint bool) string {
+func ToJson(rs []klog.Record, errs []txt.Error, warnings []string, prettyPrint 
bool) string {
        envelop := func() Envelop {
                if errs == nil {
                        return Envelop{
-                               Records: toRecordViews(rs),
-                               Errors:  nil,
+                               Records:  toRecordViews(rs),
+                               Warnings: warnings,
+                               Errors:   nil,
                        }
                } else {
                        return Envelop{
-                               Records: nil,
-                               Errors:  toErrorViews(errs),
+                               Records:  nil,
+                               Warnings: nil,
+                               Errors:   toErrorViews(errs),
                        }
                }
        }()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/parser/json/serialiser_test.go 
new/klog-7.1/klog/parser/json/serialiser_test.go
--- old/klog-7.0/klog/parser/json/serialiser_test.go    2026-02-06 
22:46:44.000000000 +0100
+++ new/klog-7.1/klog/parser/json/serialiser_test.go    2026-02-22 
11:46:19.000000000 +0100
@@ -1,27 +1,29 @@
 package json
 
 import (
+       "testing"
+
        "github.com/jotaen/klog/klog"
        "github.com/jotaen/klog/klog/parser"
        "github.com/jotaen/klog/klog/parser/txt"
        "github.com/stretchr/testify/assert"
-       "testing"
 )
 
 func TestSerialiseEmptyRecords(t *testing.T) {
-       json := ToJson([]klog.Record{}, nil, false)
-       assert.Equal(t, `{"records":[],"errors":null}`, json)
+       json := ToJson([]klog.Record{}, nil, nil, false)
+       assert.Equal(t, `{"records":[],"warnings":null,"errors":null}`, json)
 }
 
 func TestSerialiseEmptyArrayIfNoErrors(t *testing.T) {
-       json := ToJson(nil, nil, false)
-       assert.Equal(t, `{"records":[],"errors":null}`, json)
+       json := ToJson(nil, nil, nil, false)
+       assert.Equal(t, `{"records":[],"warnings":null,"errors":null}`, json)
 }
 
 func TestSerialisePrettyPrinted(t *testing.T) {
-       json := ToJson(nil, nil, true)
+       json := ToJson(nil, nil, nil, true)
        assert.Equal(t, `{
   "records": [],
+  "warnings": null,
   "errors": null
 }`, json)
 }
@@ -30,7 +32,7 @@
        json := ToJson(func() []klog.Record {
                r := klog.NewRecord(klog.Ɀ_Date_(2000, 12, 31))
                return []klog.Record{r}
-       }(), nil, false)
+       }(), nil, nil, false)
        assert.Equal(t, `{"records":[{`+
                `"date":"2000-12-31",`+
                `"summary":"",`+
@@ -42,7 +44,7 @@
                `"diff_mins":0,`+
                `"tags":[],`+
                `"entries":[]`+
-               `}],"errors":null}`, json)
+               `}],"warnings":null,"errors":null}`, json)
 }
 
 func TestSerialiseFullBlownRecord(t *testing.T) {
@@ -54,7 +56,7 @@
                r.AddRange(klog.Ɀ_Range_(klog.Ɀ_TimeYesterday_(23, 44), 
klog.Ɀ_Time_(5, 23)), nil)
                r.Start(klog.NewOpenRange(klog.Ɀ_TimeTomorrow_(0, 28)), 
klog.Ɀ_EntrySummary_("Started #todo=nr4", "still on #it"))
                return []klog.Record{r}
-       }(), nil, false)
+       }(), nil, nil, false)
        assert.Equal(t, `{"records":[{`+
                `"date":"2000-12-31",`+
                `"summary":"Hello #World\nWhat’s up?",`+
@@ -90,7 +92,7 @@
                `"start":"0:28>",`+
                `"start_mins":1468`+
                `}]`+
-               `}],"errors":null}`, json)
+               `}],"warnings":null,"errors":null}`, json)
 }
 
 func TestSerialiseParserErrors(t *testing.T) {
@@ -98,8 +100,8 @@
        json := ToJson(nil, []txt.Error{
                parser.ErrorInvalidDate().New(block, 0, 0, 10),
                parser.ErrorMalformedSummary().New(block, 1, 3, 
5).SetOrigin("/a/b/c/file.klg"),
-       }, false)
-       assert.Equal(t, `{"records":null,"errors":[{`+
+       }, nil, false)
+       assert.Equal(t, `{"records":null,"warnings":null,"errors":[{`+
                `"line":7,`+
                `"column":1,`+
                `"length":10,`+
@@ -115,3 +117,24 @@
                `"file":"/a/b/c/file.klg"`+
                `}]}`, json)
 }
+
+func TestSerialiseWarnings(t *testing.T) {
+       t.Run("include warnings if records are ok", func(t *testing.T) {
+               json := ToJson(nil, nil, []string{"Caution!", "Beware!"}, false)
+               assert.Equal(t, 
`{"records":[],"warnings":["Caution!","Beware!"],"errors":null}`, json)
+       })
+       t.Run("ignore warnings if records are not ok", func(t *testing.T) {
+               block, _ := txt.ParseBlock("2018-99-99", 6)
+               json := ToJson(nil, []txt.Error{
+                       parser.ErrorInvalidDate().New(block, 0, 0, 10),
+               }, []string{"Caution!", "Beware!"}, false)
+               assert.Equal(t, `{"records":null,"warnings":null,"errors":[{`+
+                       `"line":7,`+
+                       `"column":1,`+
+                       `"length":10,`+
+                       `"title":"Invalid date",`+
+                       `"details":"Please make sure that the date format is 
either YYYY-MM-DD or YYYY/MM/DD, and that its value represents a valid day in 
the calendar.",`+
+                       `"file":""`+
+                       `}]}`, json)
+       })
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/parser/json/view.go 
new/klog-7.1/klog/parser/json/view.go
--- old/klog-7.0/klog/parser/json/view.go       2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/parser/json/view.go       2026-02-22 11:46:19.000000000 
+0100
@@ -1,10 +1,14 @@
 package json
 
 // Envelop is the top level data structure of the JSON output.
-// It contains two nodes, `records` and `errors`, one of which is always 
`null`.
+// It contains three nodes:
+// - `records`: is `null` if there are errors
+// - `warnings`: only if applicable and only unless there are errors
+// - `errors`: only unless there are records
 type Envelop struct {
-       Records []RecordView `json:"records"`
-       Errors  []ErrorView  `json:"errors"`
+       Records  []RecordView `json:"records"`
+       Warnings []string     `json:"warnings"`
+       Errors   []ErrorView  `json:"errors"`
 }
 
 // RecordView is the JSON representation of a record.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/service/filter/filter.go 
new/klog-7.1/klog/service/filter/filter.go
--- old/klog-7.0/klog/service/filter/filter.go  2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/service/filter/filter.go  2026-02-22 11:46:19.000000000 
+0100
@@ -4,20 +4,33 @@
        "github.com/jotaen/klog/klog"
 )
 
-func Filter(p Predicate, rs []klog.Record) []klog.Record {
+// Filter goes through a list of records and only keeps those that match the
+// given predicate. The records may be returned partially, keeping only those
+// entries that match the predicate.
+// The second return value indicates whether there are partial records with a
+// should-total set, as this may yield nonsensical results in a subsequent 
evaluation.
+func Filter(p Predicate, rs []klog.Record) ([]klog.Record, bool) {
        var res []klog.Record
+       hasPartialRecordsWithShouldTotal := false
        for _, r := range rs {
-               var es []klog.Entry
-               for i, e := range r.Entries() {
-                       if p.Matches(queriedEntry{r, r.Entries()[i]}) {
-                               es = append(es, e)
+               if len(r.Entries()) == 0 && p.MatchesEmptyRecord(r) {
+                       res = append(res, r)
+               } else {
+                       var es []klog.Entry
+                       for i, e := range r.Entries() {
+                               if p.Matches(r, r.Entries()[i]) {
+                                       es = append(es, e)
+                               }
                        }
+                       if len(es) == 0 {
+                               continue
+                       }
+                       if len(es) != len(r.Entries()) && r.ShouldTotal() != 
nil {
+                               hasPartialRecordsWithShouldTotal = true
+                       }
+                       r.SetEntries(es)
+                       res = append(res, r)
                }
-               if len(es) == 0 {
-                       continue
-               }
-               r.SetEntries(es)
-               res = append(res, r)
        }
-       return res
+       return res, hasPartialRecordsWithShouldTotal
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/service/filter/filter_test.go 
new/klog-7.1/klog/service/filter/filter_test.go
--- old/klog-7.0/klog/service/filter/filter_test.go     2026-02-06 
22:46:44.000000000 +0100
+++ new/klog-7.1/klog/service/filter/filter_test.go     2026-02-22 
11:46:19.000000000 +0100
@@ -4,151 +4,272 @@
        "testing"
 
        "github.com/jotaen/klog/klog"
-       "github.com/jotaen/klog/klog/service"
+       "github.com/jotaen/klog/klog/parser"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
 )
 
 func sampleRecordsForQuerying() []klog.Record {
-       return []klog.Record{
-               func() klog.Record {
-                       // Note that records without entries never match any 
query.
-                       r := klog.NewRecord(klog.Ɀ_Date_(1999, 12, 30))
-                       r.SetSummary(klog.Ɀ_RecordSummary_("Hello World", 
"#foo"))
-                       return r
-               }(), func() klog.Record {
-                       r := klog.NewRecord(klog.Ɀ_Date_(1999, 12, 31))
-                       r.AddDuration(klog.NewDuration(5, 0), 
klog.Ɀ_EntrySummary_("#bar"))
-                       return r
-               }(), func() klog.Record {
-                       r := klog.NewRecord(klog.Ɀ_Date_(2000, 1, 1))
-                       r.SetSummary(klog.Ɀ_RecordSummary_("#foo"))
-                       r.AddDuration(klog.NewDuration(0, 15), nil)
-                       r.AddDuration(klog.NewDuration(6, 0), 
klog.Ɀ_EntrySummary_("#bar"))
-                       r.AddDuration(klog.NewDuration(0, -30), nil)
-                       return r
-               }(), func() klog.Record {
-                       r := klog.NewRecord(klog.Ɀ_Date_(2000, 1, 2))
-                       r.SetSummary(klog.Ɀ_RecordSummary_("#foo"))
-                       r.AddDuration(klog.NewDuration(7, 0), nil)
-                       return r
-               }(), func() klog.Record {
-                       r := klog.NewRecord(klog.Ɀ_Date_(2000, 1, 3))
-                       r.SetSummary(klog.Ɀ_RecordSummary_("#foo=a"))
-                       r.AddDuration(klog.NewDuration(4, 0), 
klog.Ɀ_EntrySummary_("test", "foo #bar=1"))
-                       r.AddDuration(klog.NewDuration(4, 0), 
klog.Ɀ_EntrySummary_("#bar=2"))
-                       r.Start(klog.NewOpenRange(klog.Ɀ_Time_(12, 00)), nil)
-                       return r
-               }(),
+       rs, _, err := parser.NewSerialParser().Parse(`
+1999-12-29
+No tags here
+
+1999-12-30
+Hello World #foo #first
+
+1999-12-31
+       5h #bar       [300]
+
+2000-01-01
+#foo #third
+       1:30-1:45     [15]             
+       6h #bar       [360]
+       -30m          [-30]
+
+2000-01-02
+#foo #fourth
+       7h #xyz       [420]
+
+2000-01-03
+#foo=a #fifth
+       12:00-16:00   [240]
+               #bar=1
+       3h #bar=2     [180]
+       12:00-?       [0]
+`)
+       if err != nil {
+               panic(err)
+       }
+       return rs
+}
+
+type expect struct {
+       date      klog.Date
+       durations []int
+}
+
+func assertResult(t *testing.T, es []expect, rs []klog.Record) {
+       require.Equal(t, len(es), len(rs), "unexpected number of records")
+       for i, expct := range es {
+               assert.Equal(t, expct.date, rs[i].Date(), "unexpected date")
+               require.Equal(t, len(expct.durations), len(rs[i].Entries()), 
"unexpected number of entries")
+               actualDurations := make([]int, len(rs[i].Entries()))
+               for j, e := range rs[i].Entries() {
+                       actualDurations[j] = e.Duration().InMinutes()
+               }
+               assert.Equal(t, expct.durations, actualDurations, "unexpected 
duration")
        }
 }
 
 func TestQueryWithNoClauses(t *testing.T) {
-       rs := Filter(And{}, sampleRecordsForQuerying())
-       require.Len(t, rs, 4)
-       assert.Equal(t, klog.NewDuration(5+6+7+8, -30+15), service.Total(rs...))
+       rs, hprws := Filter(And{}, sampleRecordsForQuerying())
+       assert.False(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(1999, 12, 29), []int{}},
+               {klog.Ɀ_Date_(1999, 12, 30), []int{}},
+               {klog.Ɀ_Date_(1999, 12, 31), []int{300}},
+               {klog.Ɀ_Date_(2000, 1, 1), []int{15, 360, -30}},
+               {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180, 0}},
+       }, rs)
+}
+
+func TestQueryWithNoMatches(t *testing.T) {
+       rs, hprws := Filter(IsInDateRange{
+               From: klog.Ɀ_Date_(2002, 1, 1),
+               To:   klog.Ɀ_Date_(2002, 1, 1),
+       }, sampleRecordsForQuerying())
+       assert.False(t, hprws)
+       assertResult(t, []expect{}, rs)
+}
+
+func TestQueryAgainstEmptyInput(t *testing.T) {
+       rs, hprws := Filter(IsInDateRange{
+               From: klog.Ɀ_Date_(2002, 1, 1),
+               To:   klog.Ɀ_Date_(2002, 1, 1),
+       }, nil)
+       assert.False(t, hprws)
+       assertResult(t, []expect{}, rs)
 }
 
 func TestQueryWithAtDate(t *testing.T) {
-       rs := Filter(IsInDateRange{
+       rs, hprws := Filter(IsInDateRange{
                From: klog.Ɀ_Date_(2000, 1, 2),
                To:   klog.Ɀ_Date_(2000, 1, 2),
        }, sampleRecordsForQuerying())
-       require.Len(t, rs, 1)
-       assert.Equal(t, klog.NewDuration(7, 0), service.Total(rs...))
+       assert.False(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+       }, rs)
 }
 
 func TestQueryWithAfter(t *testing.T) {
-       rs := Filter(IsInDateRange{
+       rs, hprws := Filter(IsInDateRange{
                From: klog.Ɀ_Date_(2000, 1, 1),
                To:   nil,
        }, sampleRecordsForQuerying())
-       require.Len(t, rs, 3)
-       assert.Equal(t, 1, rs[0].Date().Day())
-       assert.Equal(t, 2, rs[1].Date().Day())
-       assert.Equal(t, 3, rs[2].Date().Day())
+       assert.False(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(2000, 1, 1), []int{15, 360, -30}},
+               {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180, 0}},
+       }, rs)
 }
 
 func TestQueryWithBefore(t *testing.T) {
-       rs := Filter(IsInDateRange{
+       rs, hprws := Filter(IsInDateRange{
                From: nil,
                To:   klog.Ɀ_Date_(2000, 1, 1),
        }, sampleRecordsForQuerying())
-       require.Len(t, rs, 2)
-       assert.Equal(t, 31, rs[0].Date().Day())
-       assert.Equal(t, 1, rs[1].Date().Day())
+       assert.False(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(1999, 12, 29), []int{}},
+               {klog.Ɀ_Date_(1999, 12, 30), []int{}},
+               {klog.Ɀ_Date_(1999, 12, 31), []int{300}},
+               {klog.Ɀ_Date_(2000, 1, 1), []int{15, 360, -30}},
+       }, rs)
 }
 
-func TestQueryWithTagOnEntries(t *testing.T) {
-       rs := Filter(HasTag{klog.NewTagOrPanic("bar", "")}, 
sampleRecordsForQuerying())
-       require.Len(t, rs, 3)
-       assert.Equal(t, 31, rs[0].Date().Day())
-       assert.Equal(t, 1, rs[1].Date().Day())
-       assert.Equal(t, 3, rs[2].Date().Day())
-       assert.Equal(t, klog.NewDuration(5+8+6, 0), service.Total(rs...))
+func TestQueryWithTagOnOverallSummary(t *testing.T) {
+       rs, hprws := Filter(HasTag{klog.NewTagOrPanic("foo", "")}, 
sampleRecordsForQuerying())
+       assert.False(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(1999, 12, 30), []int{}},
+               {klog.Ɀ_Date_(2000, 1, 1), []int{15, 360, -30}},
+               {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180, 0}},
+       }, rs)
 }
 
-func TestQueryWithTagOnOverallSummary(t *testing.T) {
-       rs := Filter(HasTag{klog.NewTagOrPanic("foo", "")}, 
sampleRecordsForQuerying())
-       require.Len(t, rs, 3)
-       assert.Equal(t, 1, rs[0].Date().Day())
-       assert.Equal(t, 2, rs[1].Date().Day())
-       assert.Equal(t, 3, rs[2].Date().Day())
-       assert.Equal(t, klog.NewDuration(6+7+8, -30+15), service.Total(rs...))
+func TestQueryWithTagOnEntries(t *testing.T) {
+       rs, hprws := Filter(HasTag{klog.NewTagOrPanic("bar", "")}, 
sampleRecordsForQuerying())
+       assert.True(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(1999, 12, 31), []int{300}},
+               {klog.Ɀ_Date_(2000, 1, 1), []int{360}},
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180}},
+       }, rs)
 }
 
 func TestQueryWithTagOnEntriesAndInSummary(t *testing.T) {
-       rs := Filter(And{[]Predicate{HasTag{klog.NewTagOrPanic("foo", "")}, 
HasTag{klog.NewTagOrPanic("bar", "")}}}, sampleRecordsForQuerying())
-       require.Len(t, rs, 2)
-       assert.Equal(t, 1, rs[0].Date().Day())
-       assert.Equal(t, 3, rs[1].Date().Day())
-       assert.Equal(t, klog.NewDuration(8+6, 0), service.Total(rs...))
+       rs, hprws := Filter(And{[]Predicate{HasTag{klog.NewTagOrPanic("foo", 
"")}, HasTag{klog.NewTagOrPanic("bar", "")}}}, sampleRecordsForQuerying())
+       assert.True(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(2000, 1, 1), []int{360}},
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180}},
+       }, rs)
 }
 
 func TestQueryWithTagValues(t *testing.T) {
-       rs := Filter(HasTag{klog.NewTagOrPanic("foo", "a")}, 
sampleRecordsForQuerying())
-       require.Len(t, rs, 1)
-       assert.Equal(t, 3, rs[0].Date().Day())
-       assert.Equal(t, klog.NewDuration(8, 0), service.Total(rs...))
+       rs, hprws := Filter(HasTag{klog.NewTagOrPanic("foo", "a")}, 
sampleRecordsForQuerying())
+       assert.False(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180, 0}},
+       }, rs)
 }
 
 func TestQueryWithTagValuesInEntries(t *testing.T) {
-       rs := Filter(HasTag{klog.NewTagOrPanic("bar", "1")}, 
sampleRecordsForQuerying())
-       require.Len(t, rs, 1)
-       assert.Equal(t, 3, rs[0].Date().Day())
-       assert.Equal(t, klog.NewDuration(4, 0), service.Total(rs...))
+       rs, hprws := Filter(HasTag{klog.NewTagOrPanic("bar", "1")}, 
sampleRecordsForQuerying())
+       assert.True(t, hprws)
+       assertResult(t, []expect{
+               {klog.Ɀ_Date_(2000, 1, 3), []int{240}},
+       }, rs)
 }
 
 func TestQueryWithTagNonMatchingValues(t *testing.T) {
-       rs := Filter(HasTag{klog.NewTagOrPanic("bar", "3")}, 
sampleRecordsForQuerying())
-       require.Len(t, rs, 0)
+       rs, hprws := Filter(HasTag{klog.NewTagOrPanic("bar", "3")}, 
sampleRecordsForQuerying())
+       assert.False(t, hprws)
+       assertResult(t, []expect{}, rs)
 }
 
 func TestQueryWithEntryTypes(t *testing.T) {
        {
-               rs := Filter(IsEntryType{ENTRY_TYPE_DURATION}, 
sampleRecordsForQuerying())
-               require.Len(t, rs, 4)
-               assert.Equal(t, klog.NewDuration(0, 1545), service.Total(rs...))
+               rs, hprws := Filter(IsEntryType{ENTRY_TYPE_DURATION}, 
sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(1999, 12, 31), []int{300}},
+                       {klog.Ɀ_Date_(2000, 1, 1), []int{360, -30}},
+                       {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{180}},
+               }, rs)
+       }
+       {
+               rs, hprws := Filter(IsEntryType{ENTRY_TYPE_DURATION_NEGATIVE}, 
sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(2000, 1, 1), []int{-30}},
+               }, rs)
+       }
+       {
+               rs, hprws := Filter(IsEntryType{ENTRY_TYPE_DURATION_POSITIVE}, 
sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(1999, 12, 31), []int{300}},
+                       {klog.Ɀ_Date_(2000, 1, 1), []int{360}},
+                       {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{180}},
+               }, rs)
        }
        {
-               rs := Filter(IsEntryType{ENTRY_TYPE_DURATION_NEGATIVE}, 
sampleRecordsForQuerying())
-               require.Len(t, rs, 1)
-               assert.Equal(t, 1, rs[0].Date().Day())
-               assert.Equal(t, klog.NewDuration(0, -30), service.Total(rs...))
+               rs, hprws := Filter(IsEntryType{ENTRY_TYPE_RANGE}, 
sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(2000, 1, 1), []int{15}},
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{240}},
+               }, rs)
        }
        {
-               rs := Filter(IsEntryType{ENTRY_TYPE_DURATION_POSITIVE}, 
sampleRecordsForQuerying())
-               require.Len(t, rs, 4)
-               assert.Equal(t, klog.NewDuration(0, 1575), service.Total(rs...))
+               rs, hprws := Filter(IsEntryType{ENTRY_TYPE_OPEN_RANGE}, 
sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{0}},
+               }, rs)
+       }
+}
+
+func TestComplexFilterQueries(t *testing.T) {
+       {
+               rs, hprws := Filter(Or{[]Predicate{
+                       IsInDateRange{From: klog.Ɀ_Date_(2000, 1, 2), To: nil},
+                       HasTag{klog.NewTagOrPanic("first", "")},
+                       And{[]Predicate{
+                               Not{HasTag{klog.NewTagOrPanic("something", 
"1")}},
+                               IsEntryType{ENTRY_TYPE_RANGE},
+                       }},
+               }}, sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(1999, 12, 30), []int{}},
+                       {klog.Ɀ_Date_(2000, 1, 1), []int{15}},
+                       {klog.Ɀ_Date_(2000, 1, 2), []int{420}},
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{240, 180, 0}},
+               }, rs)
        }
        {
-               rs := Filter(IsEntryType{ENTRY_TYPE_RANGE}, 
sampleRecordsForQuerying())
-               require.Len(t, rs, 0)
-               assert.Equal(t, klog.NewDuration(0, 0), service.Total(rs...))
+               rs, hprws := Filter(And{[]Predicate{
+                       IsInDateRange{From: klog.Ɀ_Date_(2000, 1, 1), To: 
klog.Ɀ_Date_(2000, 1, 3)},
+                       HasTag{klog.NewTagOrPanic("bar", "")},
+                       Not{HasTag{klog.NewTagOrPanic("third", "")}},
+                       IsEntryType{ENTRY_TYPE_RANGE},
+               }}, sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{240}},
+               }, rs)
        }
        {
-               rs := Filter(IsEntryType{ENTRY_TYPE_OPEN_RANGE}, 
sampleRecordsForQuerying())
-               require.Len(t, rs, 1)
-               assert.Equal(t, klog.NewDuration(0, 0), service.Total(rs...))
+               rs, hprws := Filter(Not{Or{[]Predicate{
+                       IsInDateRange{From: klog.Ɀ_Date_(1999, 12, 30), To: 
klog.Ɀ_Date_(2000, 1, 1)},
+                       HasTag{klog.NewTagOrPanic("xyz", "")},
+                       And{[]Predicate{
+                               IsEntryType{ENTRY_TYPE_DURATION_POSITIVE},
+                               HasTag{klog.NewTagOrPanic("bar", "")},
+                       }},
+               }}}, sampleRecordsForQuerying())
+               assert.True(t, hprws)
+               assertResult(t, []expect{
+                       {klog.Ɀ_Date_(1999, 12, 29), []int{}},
+                       {klog.Ɀ_Date_(2000, 1, 3), []int{240, 0}},
+               }, rs)
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/service/filter/predicate.go 
new/klog-7.1/klog/service/filter/predicate.go
--- old/klog-7.0/klog/service/filter/predicate.go       2026-02-06 
22:46:44.000000000 +0100
+++ new/klog-7.1/klog/service/filter/predicate.go       2026-02-22 
11:46:19.000000000 +0100
@@ -7,32 +7,37 @@
        "github.com/jotaen/klog/klog"
 )
 
-type queriedEntry struct {
-       parent klog.Record
-       entry  klog.Entry
-}
-
+// Predicate is the generic base type for all predicates. The caller is 
responsible for
+// selecting the applicable match function (it’s meant to be either/or).
 type Predicate interface {
-       Matches(queriedEntry) bool
+       // Matches returns true if the record’s entry satisfies the predicate.
+       Matches(klog.Record, klog.Entry) bool
+       // MatchesEmptyRecord returns true if an empty record (without any 
entries)
+       // satisfies the predicate.
+       MatchesEmptyRecord(klog.Record) bool
 }
 
 type IsInDateRange struct {
-       From klog.Date
-       To   klog.Date
+       From klog.Date // May be nil to denote open range.
+       To   klog.Date // May be nil to denote open range.
+}
+
+func (i IsInDateRange) Matches(r klog.Record, e klog.Entry) bool {
+       return i.MatchesEmptyRecord(r)
 }
 
-func (i IsInDateRange) Matches(e queriedEntry) bool {
+func (i IsInDateRange) MatchesEmptyRecord(r klog.Record) bool {
        isAfter := func() bool {
                if i.From == nil {
                        return true
                }
-               return e.parent.Date().IsAfterOrEqual(i.From)
+               return r.Date().IsAfterOrEqual(i.From)
        }()
        isBefore := func() bool {
                if i.To == nil {
                        return true
                }
-               return i.To.IsAfterOrEqual(e.parent.Date())
+               return i.To.IsAfterOrEqual(r.Date())
        }()
        return isAfter && isBefore
 }
@@ -41,17 +46,30 @@
        Tag klog.Tag
 }
 
-func (h HasTag) Matches(e queriedEntry) bool {
-       return e.parent.Summary().Tags().Contains(h.Tag) || 
e.entry.Summary().Tags().Contains(h.Tag)
+func (h HasTag) Matches(r klog.Record, e klog.Entry) bool {
+       return h.MatchesEmptyRecord(r) || e.Summary().Tags().Contains(h.Tag)
+}
+
+func (h HasTag) MatchesEmptyRecord(r klog.Record) bool {
+       return r.Summary().Tags().Contains(h.Tag)
 }
 
 type And struct {
        Predicates []Predicate
 }
 
-func (a And) Matches(e queriedEntry) bool {
+func (a And) Matches(r klog.Record, e klog.Entry) bool {
+       for _, p := range a.Predicates {
+               if !p.Matches(r, e) {
+                       return false
+               }
+       }
+       return true
+}
+
+func (a And) MatchesEmptyRecord(r klog.Record) bool {
        for _, p := range a.Predicates {
-               if !p.Matches(e) {
+               if !p.MatchesEmptyRecord(r) {
                        return false
                }
        }
@@ -62,9 +80,18 @@
        Predicates []Predicate
 }
 
-func (o Or) Matches(e queriedEntry) bool {
+func (o Or) Matches(r klog.Record, e klog.Entry) bool {
        for _, p := range o.Predicates {
-               if p.Matches(e) {
+               if p.Matches(r, e) {
+                       return true
+               }
+       }
+       return false
+}
+
+func (o Or) MatchesEmptyRecord(r klog.Record) bool {
+       for _, p := range o.Predicates {
+               if p.MatchesEmptyRecord(r) {
                        return true
                }
        }
@@ -75,8 +102,12 @@
        Predicate Predicate
 }
 
-func (n Not) Matches(e queriedEntry) bool {
-       return !n.Predicate.Matches(e)
+func (n Not) Matches(r klog.Record, e klog.Entry) bool {
+       return !n.Predicate.Matches(r, e)
+}
+
+func (n Not) MatchesEmptyRecord(r klog.Record) bool {
+       return !n.Predicate.MatchesEmptyRecord(r)
 }
 
 type EntryType string
@@ -108,17 +139,17 @@
        Type EntryType
 }
 
-func (t IsEntryType) Matches(e queriedEntry) bool {
-       return klog.Unbox[bool](&e.entry, func(r klog.Range) bool {
+func (t IsEntryType) Matches(r klog.Record, e klog.Entry) bool {
+       return klog.Unbox[bool](&e, func(r klog.Range) bool {
                return t.Type == ENTRY_TYPE_RANGE
        }, func(duration klog.Duration) bool {
                if t.Type == ENTRY_TYPE_DURATION {
                        return true
                }
-               if t.Type == ENTRY_TYPE_DURATION_POSITIVE && 
e.entry.Duration().InMinutes() >= 0 {
+               if t.Type == ENTRY_TYPE_DURATION_POSITIVE && 
e.Duration().InMinutes() >= 0 {
                        return true
                }
-               if t.Type == ENTRY_TYPE_DURATION_NEGATIVE && 
e.entry.Duration().InMinutes() < 0 {
+               if t.Type == ENTRY_TYPE_DURATION_NEGATIVE && 
e.Duration().InMinutes() < 0 {
                        return true
                }
                return false
@@ -126,3 +157,7 @@
                return t.Type == ENTRY_TYPE_OPEN_RANGE
        })
 }
+
+func (t IsEntryType) MatchesEmptyRecord(r klog.Record) bool {
+       return false
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/service/warning.go 
new/klog-7.1/klog/service/warning.go
--- old/klog-7.0/klog/service/warning.go        2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/service/warning.go        2026-02-22 11:46:19.000000000 
+0100
@@ -7,21 +7,22 @@
        "github.com/jotaen/klog/klog"
 )
 
-// Warning contains information for helping locate an issue.
-type Warning struct {
-       date   klog.Date
-       origin checker
+// UsageWarning contains information for avoiding potential usage issues.
+type UsageWarning struct {
+       Name    string
+       Message string
 }
 
-// Date is the date of the record that the warning refers to.
-func (w Warning) Date() klog.Date {
-       return w.date
-}
-
-// Warning is a short description of the problem.
-func (w Warning) Warning() string {
-       return w.origin.Message()
-}
+var (
+       PointlessNowWarning = UsageWarning{
+               Name:    "POINTLESS_NOW",
+               Message: "You specified --now, but there was no open-ended time 
range",
+       }
+       EntryFilteredDiffWarning = UsageWarning{
+               Name:    "ENTRY_FILTERED_DIFFING",
+               Message: "Combining --diff and filtering at entry-level may 
yield nonsensical results",
+       }
+)
 
 type checker interface {
        Warn(klog.Record) klog.Date
@@ -39,6 +40,8 @@
                (&futureEntriesChecker{}).Name():         false,
                (&overlappingTimeRangesChecker{}).Name(): false,
                (&moreThan24HoursChecker{}).Name():       false,
+               PointlessNowWarning.Name:                 false,
+               EntryFilteredDiffWarning.Name:            false,
        }
 }
 
@@ -47,7 +50,7 @@
 // strict validation, but the main purpose is to help users spot accidental 
mistakes users
 // might have made. The checks are limited to record-level, because otherwise 
it would
 // need to make assumptions on how records are organised within or across 
files.
-func CheckForWarnings(onWarn func(Warning), reference gotime.Time, rs 
[]klog.Record, disabledCheckers DisabledCheckers) {
+func CheckForWarnings(reference gotime.Time, rs []klog.Record, 
disabledCheckers DisabledCheckers) []string {
        now := NewDateTimeFromGo(reference)
        sortedRs := Sort(rs, false)
        checkers := []checker{
@@ -56,6 +59,7 @@
                &overlappingTimeRangesChecker{},
                &moreThan24HoursChecker{},
        }
+       var warnings []string
        for _, r := range sortedRs {
                for _, c := range checkers {
                        if disabledCheckers[c.Name()] {
@@ -63,13 +67,11 @@
                        }
                        d := c.Warn(r)
                        if d != nil {
-                               onWarn(Warning{
-                                       date:   d,
-                                       origin: c,
-                               })
+                               warnings = append(warnings, d.ToString()+": 
"+c.Message())
                        }
                }
        }
+       return warnings
 }
 
 type unclosedOpenRangeChecker struct {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/klog-7.0/klog/service/warning_test.go 
new/klog-7.1/klog/service/warning_test.go
--- old/klog-7.0/klog/service/warning_test.go   2026-02-06 22:46:44.000000000 
+0100
+++ new/klog-7.1/klog/service/warning_test.go   2026-02-22 11:46:19.000000000 
+0100
@@ -1,6 +1,7 @@
 package service
 
 import (
+       "strings"
        "testing"
        gotime "time"
 
@@ -8,22 +9,18 @@
        "github.com/stretchr/testify/assert"
 )
 
-func countWarningsOfKind(c checker, ws []Warning) int {
+func countWarningsOfKind(c checker, ws []string) int {
        count := 0
        for _, w := range ws {
-               if w.Warning() == c.Message() {
+               if strings.HasSuffix(w, c.Message()) {
                        count++
                }
        }
        return count
 }
 
-func collectWarnings(reference gotime.Time, rs []klog.Record) []Warning {
-       var ws []Warning
-       CheckForWarnings(func(w Warning) {
-               ws = append(ws, w)
-       }, reference, rs, NewDisabledCheckers())
-       return ws
+func collectWarnings(reference gotime.Time, rs []klog.Record) []string {
+       return CheckForWarnings(reference, rs, NewDisabledCheckers())
 }
 
 func TestNoWarnForOpenRanges(t *testing.T) {
@@ -79,8 +76,8 @@
        }
        ws := collectWarnings(timestamp, rs)
        assert.Equal(t, 2, countWarningsOfKind(&unclosedOpenRangeChecker{}, ws))
-       assert.Equal(t, today.PlusDays(-1), ws[0].Date())
-       assert.Equal(t, today.PlusDays(-2), ws[1].Date())
+       assert.Equal(t, today.PlusDays(-1).ToString(), ws[0][0:10])
+       assert.Equal(t, today.PlusDays(-2).ToString(), ws[1][0:10])
 }
 
 func TestNoWarningForFutureEntries(t *testing.T) {
@@ -313,11 +310,7 @@
                        }(),
                }
 
-               var ws []Warning
-               CheckForWarnings(func(w Warning) {
-                       ws = append(ws, w)
-               }, timestamp, rs, x.dc)
-
+               ws := CheckForWarnings(timestamp, rs, x.dc)
                assert.Len(t, ws, x.exp)
        }
 }

++++++ klog.obsinfo ++++++
--- /var/tmp/diff_new_pack.1cSCwN/_old  2026-02-25 21:21:32.445260896 +0100
+++ /var/tmp/diff_new_pack.1cSCwN/_new  2026-02-25 21:21:32.453261225 +0100
@@ -1,5 +1,5 @@
 name: klog
-version: 7.0
-mtime: 1770414404
-commit: 76fdc21899f3547d28f6e80e4179959a92bcd999
+version: 7.1
+mtime: 1771757179
+commit: 7a4f7b6b0749adb164aa6ac67dee9ba6556f261e
 

++++++ vendor.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/alecthomas/kong/mapper.go 
new/vendor/github.com/alecthomas/kong/mapper.go
--- old/vendor/github.com/alecthomas/kong/mapper.go     2026-02-06 
22:46:44.000000000 +0100
+++ new/vendor/github.com/alecthomas/kong/mapper.go     2026-02-22 
11:46:19.000000000 +0100
@@ -541,8 +541,12 @@
                if ctx.Value.Flag != nil {
                        t := ctx.Scan.Pop()
                        // If decoding a flag, we need a value.
+                       tail := ""
+                       if sep != -1 {
+                               tail += string(sep) + "..."
+                       }
                        if t.IsEOL() {
-                               return fmt.Errorf("missing value, expecting 
\"<arg>%c...\"", sep)
+                               return fmt.Errorf("missing value, expecting 
\"<arg>%s\"", tail)
                        }
                        switch v := t.Value.(type) {
                        case string:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/github.com/alecthomas/kong/tag.go 
new/vendor/github.com/alecthomas/kong/tag.go
--- old/vendor/github.com/alecthomas/kong/tag.go        2026-02-06 
22:46:44.000000000 +0100
+++ new/vendor/github.com/alecthomas/kong/tag.go        2026-02-22 
11:46:19.000000000 +0100
@@ -166,13 +166,13 @@
        return d, nil
 }
 
-func getTagInfo(ft reflect.StructField) (string, tagChars) {
-       s, ok := ft.Tag.Lookup("kong")
+func getTagInfo(tag reflect.StructTag) (string, tagChars) {
+       s, ok := tag.Lookup("kong")
        if ok {
                return s, kongChars
        }
 
-       return string(ft.Tag), bareChars
+       return string(tag), bareChars
 }
 
 func newEmptyTag() *Tag {
@@ -204,10 +204,26 @@
                t.Ignored = true
                return t, nil
        }
-       items, err := parseTagItems(getTagInfo(ft))
+       items := map[string][]string{}
+       // First use a [Signature] if present
+       signatureTag, ok := maybeGetSignature(ft.Type)
+       if ok {
+               signatureItems, err := parseTagItems(getTagInfo(signatureTag))
+               if err != nil {
+                       return nil, err
+               }
+               items = signatureItems
+       }
+       // Next overlay the field's tags.
+       fieldItems, err := parseTagItems(getTagInfo(ft.Tag))
        if err != nil {
                return nil, err
        }
+       for key, value := range fieldItems {
+               // Prepend field tag values
+               items[key] = append(value, items[key]...)
+       }
+
        t := &Tag{
                items: items,
        }
@@ -384,3 +400,34 @@
        }
        return r, nil
 }
+
+// Signature allows flags, args and commands to supply a default set of tags,
+// that can be overridden by the field itself.
+type Signature interface {
+       // Signature returns default tags for the flag, arg or command.
+       //
+       // eg. `name:"migrate" help:"Run migrations" aliases:"mig,mg"`.
+       Signature() string
+}
+
+var signatureOverrideType = reflect.TypeOf((*Signature)(nil)).Elem()
+
+func maybeGetSignature(t reflect.Type) (reflect.StructTag, bool) {
+       ut := t
+       if ut.Kind() == reflect.Pointer {
+               ut = ut.Elem()
+       }
+       ptr := reflect.New(ut)
+       var sig string
+       for _, v := range []reflect.Value{ptr, ptr.Elem()} {
+               if v.Type().Implements(signatureOverrideType) {
+                       sig = v.Interface().(Signature).Signature() 
//nolint:forcetypeassert
+                       break
+               }
+       }
+       sig = strings.TrimSpace(sig)
+       if sig == "" {
+               return "", false
+       }
+       return reflect.StructTag(sig), true
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/github.com/jotaen/kong-completion/positional_predictor.go 
new/vendor/github.com/jotaen/kong-completion/positional_predictor.go
--- old/vendor/github.com/jotaen/kong-completion/positional_predictor.go        
2026-02-06 22:46:44.000000000 +0100
+++ new/vendor/github.com/jotaen/kong-completion/positional_predictor.go        
2026-02-22 11:46:19.000000000 +0100
@@ -8,9 +8,10 @@
 
 // PositionalPredictor is a predictor for positional arguments
 type PositionalPredictor struct {
-       Predictors []complete.Predictor
-       ArgFlags   []string
-       BoolFlags  []string
+       Predictors           []complete.Predictor
+       ArgFlags             []string
+       BoolFlags            []string
+       LastFlagIsCumulative bool // “Cumulative” means the last flag can 
appear multiple times.
 }
 
 // Predict implements complete.Predict
@@ -26,6 +27,9 @@
        position := p.predictorIndex(a)
        complete.Log("predicting positional argument(%d)", position)
        if position < 0 || position > len(p.Predictors)-1 {
+               if p.LastFlagIsCumulative && len(p.Predictors) > 0 {
+                       return p.Predictors[len(p.Predictors)-1]
+               }
                return nil
        }
        return p.Predictors[position]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/github.com/jotaen/kong-completion/prediction.go 
new/vendor/github.com/jotaen/kong-completion/prediction.go
--- old/vendor/github.com/jotaen/kong-completion/prediction.go  2026-02-06 
22:46:44.000000000 +0100
+++ new/vendor/github.com/jotaen/kong-completion/prediction.go  2026-02-22 
11:46:19.000000000 +0100
@@ -161,10 +161,12 @@
        if err != nil {
                return nil, err
        }
+       lastIsCumulative := len(node.Positional) > 0 && 
node.Positional[len(node.Positional)-1].IsCumulative()
        cmd.Args = &PositionalPredictor{
-               Predictors: pps,
-               ArgFlags:   flagNamesWithHyphens(flags.argFlags...),
-               BoolFlags:  flagNamesWithHyphens(flags.boolFlags...),
+               Predictors:           pps,
+               ArgFlags:             flagNamesWithHyphens(flags.argFlags...),
+               BoolFlags:            flagNamesWithHyphens(flags.boolFlags...),
+               LastFlagIsCumulative: lastIsCumulative,
        }
 
        return &cmd, nil
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt
--- old/vendor/modules.txt      2026-02-06 22:46:44.000000000 +0100
+++ new/vendor/modules.txt      2026-02-22 11:46:19.000000000 +0100
@@ -1,7 +1,7 @@
 # cloud.google.com/go v0.123.0
 ## explicit; go 1.24.0
 cloud.google.com/go/civil
-# github.com/alecthomas/kong v1.13.0
+# github.com/alecthomas/kong v1.14.0
 ## explicit; go 1.20
 github.com/alecthomas/kong
 # github.com/davecgh/go-spew v1.1.1
@@ -16,7 +16,7 @@
 # github.com/jotaen/genie v0.0.3
 ## explicit; go 1.25
 github.com/jotaen/genie
-# github.com/jotaen/kong-completion v0.0.11
+# github.com/jotaen/kong-completion v0.0.12
 ## explicit; go 1.25
 github.com/jotaen/kong-completion
 # github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51

Reply via email to