lucidplan-175b80bb-a55d

job succeeded

starting new job...


→ running command: cd "/etc/byggsteg/job-clone//lucidplan/trunk" && git reset --hard && git pull

HEAD is now at 942db43 chore: Update guile-veritas
From https://codeberg.org/jjba23/lucidplan
   942db43..5645584  trunk      -> origin/trunk
Updating 942db43..5645584
Fast-forward
 manifest.scm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

→ running command: cd /etc/byggsteg/job-clone/lucidplan/trunk && uname -a

Linux wolk-jjba 7.2.11-gnu #1 SMP PREEMPT_DYNAMIC 1 x86_64 GNU/Linux

→ running command: cd /etc/byggsteg/job-clone/lucidplan/trunk && git log --name-status HEAD^..HEAD

commit 5645584bd9d64148572c990897f6ec93dbb274d8
Author: Josep Bigorra <jjbigorra@gmail.com>
Date:   Mon Oct 26 13:24:37 2026 +0100

    chore: Update guile-scriba
    
    The pin moves to v0.4.5, a maintenance release: scriba's log-level suite called
    (apply test ...), which Veritas cannot expand, so its test file stopped
    compiling.  Nothing in the library's API, behaviour or documentation changed,
    so the shell this manifest describes is the same one with a test suite that
    runs again.
    
    🤖 Generated with [ECA](https://eca.dev) (deepseek/deepseek-flash)
    
    Co-Authored-By: eca-agent <git@eca.dev>

M       manifest.scm

→ running command: cd /etc/byggsteg/job-clone/lucidplan/trunk && maak test

The following derivation will be built:
  /gnu/store/1dka2q7wkmp5a4rc5lcsjxflqvn21rx9-profile.drv

building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 21 packages...

scanning directories for unit tests...
scanning present working directory for unit tests...

shuffled the files with seed 397291958 (replay with VERITAS_SEED=397291958)

found unit tests: (ticket-thread-spec.scm burndown-spec.scm ticket-estimate-spec.scm ticket-triage-spec.scm time-entry-spec.scm)


running unit tests from file: ticket-thread-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 397291958 (replay with VERITAS_SEED=397291958)
+----------------------------------------------+
[📑 suite] the type a file is served as
  [🧪 test] anything a header cannot carry becomes the generic type
    [⚙️ assert] a line break would split the header
    ✅ got: application/octet-stream
    [⚙️ assert] a quoted type is not a type
    ✅ got: application/octet-stream
    [⚙️ assert] no type at all is the generic one
    ✅ got: application/octet-stream
    [⚙️ assert] an empty type is the generic one
    ✅ got: application/octet-stream
  [🧪 test] a media type is kept
    [⚙️ assert] a type a header can carry survives
    ✅ got: text/plain
    [⚙️ assert] a type the application made up survives
    ✅ got: application/vnd.oasis.opendocument.text
+----------------------------------------------+
[📑 suite] the thread a ticket's comments make
  [🧪 test] the depth a comment is indented by is how far down it is
    [⚙️ assert] a comment on the ticket is not indented
    ✅ got: 0
    [⚙️ assert] a reply is indented once
    ✅ got: 1
    [⚙️ assert] a reply to a reply is indented twice
    ✅ got: 2
  [🧪 test] a comment nothing answers has no children
    [⚙️ assert] the last word is the last word
    ✅ got: ()
  [🧪 test] a thread that was written wrong still terminates
    [⚙️ assert] a cycle is answered at the depth reached
    ✅ got: 2
    [⚙️ assert] a missing parent is answered at the root
    ✅ got: 0
  [🧪 test] an id read back from the database names the same comment
    [⚙️ assert] a numeric parent names the same comment
    ✅ got: 1
  [🧪 test] the comments on the ticket itself are the ones it hangs from
    [⚙️ assert] the roots are the comments with no parent
    ✅ got: (c1 c4)
  [🧪 test] a reply is drawn under what it answers
    [⚙️ assert] the answer to the first word
    ✅ got: (c2)
    [⚙️ assert] nothing answers the answer but one
    ✅ got: (c3)
+----------------------------------------------+
[📑 suite] the name a file is served under
  [🧪 test] an empty name is still a name
    [⚙️ assert] nothing submitted answers the generic
    ✅ got: attachment
    [⚙️ assert] a name of nothing but space answers it too
    ✅ got: attachment
    [⚙️ assert] no name at all answers it too
    ✅ got: attachment
  [🧪 test] a name that is just a name is kept
    [⚙️ assert] a plain name survives
    ✅ got: report.pdf
  [🧪 test] a name is cut to a length a header can carry
    [⚙️ assert] a very long name is shortened
    ✅ got: #t
  [🧪 test] what a header cannot carry is replaced
    [⚙️ assert] quotes and backslashes do not survive
    ✅ got: my _report_.pdf
    [⚙️ assert] a line break is replaced
    ✅ got: line_break.txt
  [🧪 test] only the last component of a submitted path is kept
    [⚙️ assert] a path is cut to its last component
    ✅ got: passwd
    [⚙️ assert] a separator a path could be written with does not survive
    ✅ got: #f
+----------------------------------------------+
[📑 suite] how a download is named
  [🧪 test] a path in the submitted name never reaches the header
    [⚙️ assert] the plain form is cut to the last name
    ✅ got: hosts
  [🧪 test] both the plain and the encoded name are carried
    [⚙️ assert] the disposition carries the two names
    ✅ got: (attachment (filename . my report.pdf) (filename* . UTF-8''my%20report.pdf))
  [🧪 test] the disposition is a type and its parameters
    [⚙️ assert] the type is the attachment disposition
    ✅ got: attachment
+----------------------------------------------+
[📑 suite] how big a file is, said in one line
  [🧪 test] a size nobody knows is not a size
    [⚙️ assert] no size answers nothing
    ✅ got: 
  [🧪 test] larger files are said in the unit a reader expects
    [⚙️ assert] a couple of kilobytes
    ✅ got: 2.0 KB
    [⚙️ assert] a couple of megabytes
    ✅ got: 2.0 MB
  [🧪 test] bytes are said as bytes
    [⚙️ assert] a small file is counted in bytes
    ✅ got: 512 B
    [⚙️ assert] an empty file is counted too
    ✅ got: 0 B
ℹ️ info - ran 5 tasks in: 0 seconds, 58811 microseconds

running unit tests from file: burndown-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 397291958 (replay with VERITAS_SEED=397291958)
+----------------------------------------------+
[📑 suite] how a deadline stands, read against today
  [🧪 test] a ticket with no deadline has no state
    [⚙️ assert] no deadline answers nothing
    ✅ got: #f
    [⚙️ assert] a deadline that is not a day answers nothing
    ✅ got: #f
  [🧪 test] a day that has passed is overdue
    [⚙️ assert] yesterday is overdue
    ✅ got: overdue
  [🧪 test] a day further out is simply scheduled
    [⚙️ assert] the day after the warning
    ✅ got: scheduled
  [🧪 test] today is today
    [⚙️ assert] the day itself
    ✅ got: due-today
  [🧪 test] the warning is a week, and it is the same week everywhere
    [⚙️ assert] the threshold the pages share
    ✅ got: 7
  [🧪 test] a day within the warning is due soon
    [⚙️ assert] a week out is close enough to worry about
    ✅ got: due-soon
    [⚙️ assert] tomorrow is close enough too
    ✅ got: due-soon
+----------------------------------------------+
[📑 suite] the day a submitted date names
  [🧪 test] a day written the way a date field writes it is read
    [⚙️ assert] an ordinary day
    ✅ got: 2026-10-27
    [⚙️ assert] space around it is not part of it
    ✅ got: 2026-10-27
  [🧪 test] what is not a date is refused
    [⚙️ assert] an empty form is no date
    ✅ got: #f
    [⚙️ assert] no value at all is no date
    ✅ got: #f
    [⚙️ assert] a date without its day is refused
    ✅ got: #f
    [⚙️ assert] a date written the other way round is refused
    ✅ got: #f
    [⚙️ assert] a month that does not exist is refused
    ✅ got: #f
  [🧪 test] a day that does not exist is refused
    [⚙️ assert] the 31st of February is not a day
    ✅ got: #f
    [⚙️ assert] the 31st of April is not a day
    ✅ got: #f
    [⚙️ assert] the 29th of February 2026 is not a day
    ✅ got: #f
    [⚙️ assert] the 29th of February 2028 is a leap day
    ✅ got: 2028-02-29
+----------------------------------------------+
[📑 suite] counting the days between two of them
  [🧪 test] the same day is no days apart
    [⚙️ assert] a day and itself
    ✅ got: 0
  [🧪 test] the answer is negative when the second day is earlier
    [⚙️ assert] a week the other way
    ✅ got: -7
  [🧪 test] a value that is not a day is no difference
    [⚙️ assert] two things that are not days
    ✅ got: #f
  [🧪 test] a week is seven days
    [⚙️ assert] seven days
    ✅ got: 7
  [🧪 test] a day and its number are each other's
    [⚙️ assert] the number of a day names it back
    ✅ got: 2026-10-27
  [🧪 test] month lengths and leap years do not disturb the count
    [⚙️ assert] across the end of a short month
    ✅ got: 7
    [⚙️ assert] across a leap day
    ✅ got: 2
    [⚙️ assert] across a new year
    ✅ got: 2
+----------------------------------------------+
[📑 suite] the states a sprint can be in
  [🧪 test] a state nobody offers is refused
    [⚙️ assert] a state off the ladder is refused
    ✅ got: #f
    [⚙️ assert] something that is not text is refused
    ✅ got: #f
  [🧪 test] a state on the ladder is taken as it is stored
    [⚙️ assert] an ordinary state
    ✅ got: active
    [⚙️ assert] a state typed in capitals means the same
    ✅ got: closed
  [🧪 test] only one state is the one a team is in
    [⚙️ assert] active is running
    ✅ got: #t
    [⚙️ assert] planned is not
    ✅ got: #f
  [🧪 test] the three states are the shipped ladder
    [⚙️ assert] planned, active, closed
    ✅ got: (planned active closed)
  [🧪 test] a stretch of days has to run forwards
    [⚙️ assert] an end before its beginning is refused
    ✅ got: #f
    [⚙️ assert] an undated sprint is allowed
    ✅ got: #t
    [⚙️ assert] an open-ended sprint is allowed
    ✅ got: #t
+----------------------------------------------+
[📑 suite] the line a sprint is burnt down along
  [🧪 test] a sprint with no dates is not drawn
    [⚙️ assert] no days to draw
    ✅ got: ()
  [🧪 test] the summary is the last day said once
    [⚙️ assert] thirteen committed
    ✅ got: 13
    [⚙️ assert] five done
    ✅ got: 5
    [⚙️ assert] eight left
    ✅ got: 8
  [🧪 test] a day is drawn up to today and no further
    [⚙️ assert] three days of a five-day sprint
    ✅ got: (2026-10-01 2026-10-02 2026-10-03)
  [🧪 test] an unestimated ticket adds nothing to the sum
    [⚙️ assert] the unsized ticket is not counted
    ✅ got: 5
  [🧪 test] the point a day started with is the scope and what was done
    [⚙️ assert] thirteen points were committed
    ✅ got: 13
    [⚙️ assert] five of them were done
    ✅ got: 5
  [🧪 test] work finished on a day is burnt on that day, not before
    [⚙️ assert] nothing is done on the first day
    ✅ got: 13
    [⚙️ assert] five points are gone on the second
    ✅ got: 8
    [⚙️ assert] and they stay gone on the third
    ✅ got: 8
  [🧪 test] a ticket taken out of the sprint stops counting
    [⚙️ assert] the scope still holds it on the first day
    ✅ got: 13
    [⚙️ assert] it is gone from the second day on
    ✅ got: 5
    [⚙️ assert] and nothing but the done ticket is left
    ✅ got: 0
  [🧪 test] the committed line falls evenly to nothing on the last day
    [⚙️ assert] the first day is the whole commitment
    ✅ got: 13.0
    [⚙️ assert] the second day is three quarters of it
    ✅ got: 9.75
    [⚙️ assert] the third day is half of it
    ✅ got: 6.5
  [🧪 test] a sprint with nothing in it is a flat line at nothing
    [⚙️ assert] every day is zero
    ✅ got: (0 0 0)
  [🧪 test] a ticket that was reopened is not still counted as done
    [⚙️ assert] five points are burnt on the second day
    ✅ got: 8
    [⚙️ assert] and they come back when the ticket does
    ✅ got: 13
  [🧪 test] a sprint whose end is before its beginning is not drawn
    [⚙️ assert] a backwards sprint draws nothing
    ✅ got: ()
  [🧪 test] a closed sprint is drawn to its last day
    [⚙️ assert] all five days, read afterwards
    ✅ got: 5
ℹ️ info - ran 5 tasks in: 0 seconds, 93119 microseconds

running unit tests from file: ticket-estimate-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 397291958 (replay with VERITAS_SEED=397291958)
+----------------------------------------------+
[📑 suite] an estimate changed from the ticket's own page
  [🧪 test] a size nobody offered is refused
    [⚙️ assert] a size off the ladder changes nothing
    ✅ got: #f
  [🧪 test] a size the menu offered is accepted
    [⚙️ assert] the change is the column and its value
    ✅ got: ((estimate-size . m))
    [⚙️ assert] clearing a size is offered too
    ✅ got: ((estimate-size . ))
  [🧪 test] a story point nobody offered is refused
    [⚙️ assert] a value off the ladder changes nothing
    ✅ got: #f
    [⚙️ assert] words change nothing
    ✅ got: #f
  [🧪 test] a story point the menu offered is accepted
    [⚙️ assert] the change is the column and its value
    ✅ got: ((estimate-points . 8))
  [🧪 test] clearing an estimate is a change the menu offers
    [⚙️ assert] the empty row changes the ticket
    ✅ got: ((estimate-points . ))
+----------------------------------------------+
[📑 suite] a ticket's story-point estimate
  [🧪 test] the ladder is the one the menus and the endpoints share
    [⚙️ assert] the points are the shipped sequence
    ✅ got: (0 1 2 3 5 8 13 21 34)
  [🧪 test] a number on the team's ladder is taken as that number
    [⚙️ assert] a value the menu offered is accepted
    ✅ got: 5
    [⚙️ assert] a number arrives as a number as well
    ✅ got: 13
    [⚙️ assert] space around it is not part of it
    ✅ got: 3
    [⚙️ assert] zero is an estimate somebody made
    ✅ got: 0
  [🧪 test] an estimate nobody made is no estimate, not zero
    [⚙️ assert] an empty form is no estimate
    ✅ got: #f
    [⚙️ assert] a blank form is no estimate
    ✅ got: #f
    [⚙️ assert] no value at all is no estimate
    ✅ got: #f
  [🧪 test] a number the team does not estimate with is refused
    [⚙️ assert] a value off the ladder is refused
    ✅ got: #f
    [⚙️ assert] a negative estimate is refused
    ✅ got: #f
    [⚙️ assert] words are refused
    ✅ got: #f
    [⚙️ assert] something that is not text is refused
    ✅ got: #f
+----------------------------------------------+
[📑 suite] a ticket's t-shirt size
  [🧪 test] no size is no estimate
    [⚙️ assert] an empty form is no size
    ✅ got: #f
    [⚙️ assert] a value that is not text is no size
    ✅ got: #f
  [🧪 test] each size is shown to a reader in capitals
    [⚙️ assert] the smallest is shown in capitals
    ✅ got: XS
    [⚙️ assert] the middle of the ladder
    ✅ got: M
    [⚙️ assert] the largest is shown in capitals
    ✅ got: XXL
    [⚙️ assert] a size nobody knows is shown as itself
    ✅ got: huge
  [🧪 test] a size nobody offers is refused
    [⚙️ assert] a size off the ladder is refused
    ✅ got: #f
    [⚙️ assert] the ladder is the shipped sequence
    ✅ got: (xs s m l xl xxl)
  [🧪 test] a size on the ladder is taken as it is stored
    [⚙️ assert] a size the menu offered is accepted
    ✅ got: l
    [⚙️ assert] a size typed in capitals means the same
    ✅ got: xl
    [⚙️ assert] space around it is not part of it
    ✅ got: m
+----------------------------------------------+
[📑 suite] the changes a write to a ticket is recorded with
  [🧪 test] an estimate that was cleared is a change
    [⚙️ assert] the estimate it had is recorded
    ✅ got: (3 )
  [🧪 test] every tracked field has the name a reader knows it by
    [⚙️ assert] the seven tracked fields are named
    ✅ got: ((title title) (description description) (ticket-status status) (ticket-type type) (priority priority) (estimate-points story points) (estimate-size size))
  [🧪 test] a field that moved is recorded with its two values
    [⚙️ assert] the title, the label and both values
    ✅ got: ((title title a ticket another ticket))
  [🧪 test] several fields that moved are recorded one row each
    [⚙️ assert] both estimates moved
    ✅ got: 2
  [🧪 test] a column the tracker does not watch is not a change
    [⚙️ assert] the stamp is not a tracked field
    ✅ got: #f
  [🧪 test] a ticket submitted back unchanged is not an edit
    [⚙️ assert] nothing moved, so nothing is recorded
    ✅ got: ()
  [🧪 test] the two ways of saying nothing are the same state
    [⚙️ assert] a missing value and an empty text agree
    ✅ got: 
    [⚙️ assert] and both are recorded as the empty text
    ✅ got: 
    [⚙️ assert] a number is recorded as its text
    ✅ got: 21
  [🧪 test] an estimate that was never made is not a change
    [⚙️ assert] no estimate answers no change
    ✅ got: ()
ℹ️ info - ran 4 tasks in: 0 seconds, 54269 microseconds

running unit tests from file: ticket-triage-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 397291958 (replay with VERITAS_SEED=397291958)
+----------------------------------------------+
[📑 suite] a status that is not on the list
  [🧪 test] reads as itself when there is no label for it
    [⚙️ assert] the value stands in for the label
    ✅ got: polishing
    [⚙️ assert] there is no spec for it
    ✅ got: #f
  [🧪 test] is not in any bucket's list
    [⚙️ assert] the done bucket holds the statuses that are done
    ✅ got: (done)
  [🧪 test] is still a ticket somebody is working on
    [⚙️ assert] an unknown status is bucketed as open
    ✅ got: open
+----------------------------------------------+
[📑 suite] statuses written by hand
  [🧪 test] a note and a blank line are skipped rather than read as statuses
    [⚙️ assert] the comment is not a status
    ✅ got: #f
    [⚙️ assert] no status was invented by the blank line
    ✅ got: 3
  [🧪 test] the values are read in the order they were written
    [⚙️ assert] the read-back values keep their order
    ✅ got: (todo done rejected)
  [🧪 test] the bucket is read
    [⚙️ assert] a status in the done bucket says so
    ✅ got: done
    [⚙️ assert] a status in the rejected bucket says so
    ✅ got: rejected
  [🧪 test] a field left out takes the default the rest of the code reads
    [⚙️ assert] a bucket nothing knows is read as open
    ✅ got: open
  [🧪 test] each field of an entry is read
    [⚙️ assert] the label is read
    ✅ got: To-do
    [⚙️ assert] the icon is read
    ✅ got: anchor
    [⚙️ assert] the glyph is read
    ✅ got: ⚓
  [🧪 test] text that names nothing is not a list of statuses
    [⚙️ assert] an empty block answers #f
    ✅ got: #f
    [⚙️ assert] a block of notes alone answers #f
    ✅ got: #f
+----------------------------------------------+
[📑 suite] the order a list is asked for in
  [🧪 test] a keyword the pages offer becomes an ORDER BY
    [⚙️ assert] the title, A to Z
    ✅ got: ((title asc))
    [⚙️ assert] the priority, highest first
    ✅ got: ((priority desc))
  [🧪 test] a keyword nobody offers falls back to the last edited first
    [⚙️ assert] an unknown order is the default one
    ✅ got: ((updated-at desc))
    [⚙️ assert] and so is no order at all
    ✅ got: ((updated-at desc))
+----------------------------------------------+
[📑 suite] the list a project's tickets move through
  [🧪 test] a project with none uses the instance's
    [⚙️ assert] a project that says nothing falls back to the instance
    ✅ got: ((todo To-do anchor ⚓ open) (work-in-progress Work in Progress gear-fine ⚙️ open) (in-review In Review list-magnifying-glass 🔍 open) (done Done check ✅ done) (rejected Rejected x ❌ rejected))
  [🧪 test] a project that could not be read is not an error
    [⚙️ assert] no project at all falls back to the instance
    ✅ got: ((todo To-do anchor ⚓ open) (work-in-progress Work in Progress gear-fine ⚙️ open) (in-review In Review list-magnifying-glass 🔍 open) (done Done check ✅ done) (rejected Rejected x ❌ rejected))
  [🧪 test] a project with a list of its own uses it
    [⚙️ assert] the project's own list is the one read
    ✅ got: (todo shipped)
+----------------------------------------------+
[📑 suite] the assignee a form submitted
  [🧪 test] no assignee is the absence of one, not an unknown user
    [⚙️ assert] an empty submission leaves the ticket as it was
    ✅ got: #f
  [🧪 test] an id that was offered is answered
    [⚙️ assert] a known id is taken as it was submitted
    ✅ got: user-1
  [🧪 test] an id that was not offered is refused
    [⚙️ assert] an id no menu offered is not an assignee
    ✅ got: #f
    [⚙️ assert] a value that is not text is not an assignee
    ✅ got: #f
+----------------------------------------------+
[📑 suite] an inline change from the ticket's own page
  [🧪 test] with no project list the instance's own is the ladder
    [⚙️ assert] the first status of the instance's list is accepted
    ✅ got: #t
  [🧪 test] a status the project moves through is accepted
    [⚙️ assert] the change is the column and its new value
    ✅ got: ((ticket-status . done))
  [🧪 test] a type is checked against the types a ticket has
    [⚙️ assert] a type on the ladder is accepted
    ✅ got: ((ticket-type . bug))
    [⚙️ assert] a type nothing offers is refused
    ✅ got: #f
    [⚙️ assert] the types are the ones the schema holds
    ✅ got: (task bug story spike time-box)
  [🧪 test] a field a ticket has no ladder for is refused
    [⚙️ assert] the priority is not changed this way
    ✅ got: #f
  [🧪 test] a status the project does not move through is refused
    [⚙️ assert] a status invented by hand changes nothing
    ✅ got: #f
ℹ️ info - ran 6 tasks in: 0 seconds, 52258 microseconds

running unit tests from file: time-entry-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 397291958 (replay with VERITAS_SEED=397291958)
+----------------------------------------------+
[📑 suite] a number of minutes, said to a reader
  [🧪 test] less than an hour is said in minutes
    [⚙️ assert] three quarters of an hour
    ✅ got: 45m
  [🧪 test] a number that is not a duration is said as nothing
    [⚙️ assert] no value answers the empty text
    ✅ got: 
    [⚙️ assert] a negative duration answers it too
    ✅ got: 
  [🧪 test] a whole number of hours is said in hours
    [⚙️ assert] two hours
    ✅ got: 2h
  [🧪 test] hours and minutes are said together
    [⚙️ assert] an hour and a half
    ✅ got: 1h 30m
  [🧪 test] nothing is said as nothing rather than as empty space
    [⚙️ assert] zero minutes still reads as a duration
    ✅ got: 0m
+----------------------------------------------+
[📑 suite] a duration, read the ways it is written
  [🧪 test] the case of what was typed does not matter
    [⚙️ assert] capitals are the same duration
    ✅ got: 90
  [🧪 test] a duration is read in the words it is spoken in
    [⚙️ assert] two hours, spelled out
    ✅ got: 120
    [⚙️ assert] three quarters of an hour
    ✅ got: 45
    [⚙️ assert] an abbreviation of an hour
    ✅ got: 60
    [⚙️ assert] an abbreviation of minutes
    ✅ got: 15
    [⚙️ assert] a whole day of work is a day
    ✅ got: 1440
  [🧪 test] an hour and a half is ninety minutes however it is typed
    [⚙️ assert] in hours, with a fraction
    ✅ got: 90
    [⚙️ assert] in minutes alone
    ✅ got: 90
    [⚙️ assert] as hours and minutes, run together
    ✅ got: 90
    [⚙️ assert] as hours and minutes, with a space
    ✅ got: 90
    [⚙️ assert] as a clock reading
    ✅ got: 90
    [⚙️ assert] as a bare number of minutes
    ✅ got: 90
  [🧪 test] space around the whole thing is not part of it
    [⚙️ assert] a padded form still means its duration
    ✅ got: 30
+----------------------------------------------+
[📑 suite] what is not a duration
  [🧪 test] a clock reading is read as a clock, not as two numbers
    [⚙️ assert] minutes past the hour are minutes
    ✅ got: #f
    [⚙️ assert] a single minute digit is not a clock
    ✅ got: #f
  [🧪 test] a day's worth is the ceiling
    [⚙️ assert] more than a day is a typo
    ✅ got: #f
    [⚙️ assert] a bare number past the ceiling is a typo
    ✅ got: #f
  [🧪 test] what names no duration at all is refused
    [⚙️ assert] words are refused
    ✅ got: #f
    [⚙️ assert] a negative duration is refused
    ✅ got: #f
    [⚙️ assert] a unit nobody knows is refused
    ✅ got: #f
    [⚙️ assert] a stray character is not skipped
    ✅ got: #f
    [⚙️ assert] something that is not text is refused
    ✅ got: #f
  [🧪 test] no time logged is no entry
    [⚙️ assert] an empty form is refused
    ✅ got: #f
    [⚙️ assert] a form of nothing but space is refused
    ✅ got: #f
    [⚙️ assert] no value at all is refused
    ✅ got: #f
    [⚙️ assert] zero minutes is not time spent
    ✅ got: #f
    [⚙️ assert] zero hours is not time spent either
    ✅ got: #f
+----------------------------------------------+
[📑 suite] a number of minutes, added up in hours
  [🧪 test] part of an hour is a fraction of it
    [⚙️ assert] an hour and a half is 1.5
    ✅ got: 1.5;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling test/veritas/unit/ticket-thread-spec.scm
;;; compiling lib/lucidplan/domain/ticket-comment.scm
;;; compiling lib/lucidplan/domain/repo.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/lib/lucidplan/domain/repo.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/lib/lucidplan/domain/ticket-comment.scm.go
;;; compiling lib/lucidplan/attachments.scm
;;; compiling lib/lucidplan/config.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/lib/lucidplan/config.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/lib/lucidplan/attachments.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/test/veritas/unit/ticket-thread-spec.scm.go
;;; compiling test/veritas/unit/burndown-spec.scm
;;; compiling lib/lucidplan/domain.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/lib/lucidplan/domain.scm.go
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/test/veritas/unit/burndown-spec.scm.go
;;; compiling test/veritas/unit/ticket-estimate-spec.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/test/veritas/unit/ticket-estimate-spec.scm.go
;;; compiling test/veritas/unit/ticket-triage-spec.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/test/veritas/unit/ticket-triage-spec.scm.go
;;; compiling test/veritas/unit/time-entry-spec.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/test/veritas/unit/time-entry-spec.scm.go

    [⚙️ assert] three quarters is 0.75
    ✅ got: 0.75
  [🧪 test] a number that is not a duration is zero hours
    [⚙️ assert] no value answers zero
    ✅ got: 0
  [🧪 test] a whole number of hours is a whole decimal
    [⚙️ assert] an hour is 1
    ✅ got: 1
    [⚙️ assert] two hours is 2
    ✅ got: 2
+----------------------------------------------+
[📑 suite] what a ticket's time entries add up to
  [🧪 test] every entry counts toward the total
    [⚙️ assert] two and a half hours over three days
    ✅ got: 165
  [🧪 test] a ticket nobody logged time on adds up to nothing
    [⚙️ assert] no entries is no minutes
    ✅ got: 0
  [🧪 test] a total reads as a duration and as hours
    [⚙️ assert] one hundred and sixty-five minutes
    ✅ got: 2h 45m
    [⚙️ assert] and as the hours a report adds up in
    ✅ got: 2.75
  [🧪 test] a row that is not a duration does not break the sum
    [⚙️ assert] a malformed row counts as nothing
    ✅ got: 120
ℹ️ info - ran 5 tasks in: 0 seconds, 50665 microseconds

finished running tests


--------------------------------------------------------------

Maak 0.14.0: the infinitely extensible command runner

Loading tasks from Maak file: /etc/byggsteg/job-clone/lucidplan/trunk/maak.scm

Executing Maak tasks: test


--------------------------------------------------------------

=> Running Maak task: test

  Run the test suite with veritas.

The tests live under `test/veritas/': `unit/' for the ones that run on every
change, and `benchmark/' for the ones that measure rather than assert, which
veritas keeps apart because they answer a different question and take a
different amount of time.  The application's own directories are on the load
path, so a test names the module it tests the way the application does.

Executing: env VERITAS_TEST_TYPE=unit VERITAS_RUN_BENCHMARKS=true guix time-machine --channels=channels.scm -- shell -m manifest.scm -- guile --fresh-auto-compile -L . -L lib -L test -c '(begin (setlocale LC_ALL "C.UTF-8") (use-modules (veritas runner)) (run-tests))'

→ running command: cd /etc/byggsteg/job-clone/lucidplan/trunk && sudo herd enable lucidplan-daemon || true && sudo herd restart lucidplan-daemon || true

Enabled service lucidplan-daemon.
Service lucidplan-daemon has been started.

→ running command: cd /etc/byggsteg/job-clone/lucidplan/trunk && sudo maak deploy

Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
Warning (emacs): Cannot fontify source block (htmlize.el >= 1.34 required).  Falling back to plain text.  (see ‘org-html-htmlize-output-type’)
finished publishing Lucidplan manual


--------------------------------------------------------------

Maak 0.14.0: the infinitely extensible command runner

Loading tasks from Maak file: /etc/byggsteg/job-clone/lucidplan/trunk/maak.scm

Executing Maak tasks: deploy


--------------------------------------------------------------

=> Running Maak task: deploy

  Deploy the project's documentation.

  Execute command:
guix time-machine --channels=channels.scm -- shell -m manifest.scm -- emacs -Q --batch --eval '(progn (require (quote ox-html)) (setq org-html-prefer-user-labels t) (find-file "./README.org") (org-html-export-to-html) (message "finished publishing Lucidplan manual"))'

Executing: guix time-machine --channels=channels.scm -- shell -m manifest.scm -- emacs -Q --batch --eval '(progn (require (quote ox-html)) (setq org-html-prefer-user-labels t) (find-file "./README.org") (org-html-export-to-html) (message "finished publishing Lucidplan manual"))'
removed '/srv/http/jointhefreeworld.org/manuals/lucidplan/index.html'
renamed 'README.html' -> '/srv/http/jointhefreeworld.org/manuals/lucidplan/index.html'
inspect the job's lambda expression
(lambda()
  `((task . ("maak-test" "pull-and-restart" "maak-deploy"))
    (daemon-name . "lucidplan-daemon")
    (project . "lucidplan")
    (branch-name . "trunk")
    (clone-url . "https://codeberg.org/jjba23/lucidplan.git")))