lucidplan-5055f632-705c

job succeeded

starting new job...


→ running command: dir='/etc/byggsteg/job-clone//lucidplan/trunk'
branch='trunk'
if ! command -v git >/dev/null 2>&1; then
  echo 'byggsteg: git is not on this deployment PATH, so the clone cannot be updated' >&2
  exit 1
fi
if ! cd "$dir"; then
  echo "byggsteg: cannot enter the clone $dir" >&2
  exit 1
fi
root=$(git rev-parse --show-toplevel 2>/dev/null)
if [ "$root" != "$(pwd -P)" ]; then
  echo "byggsteg: $dir is not a git checkout; remove it and the next run clones it again" >&2
  exit 1
fi
if ! origin=$(git remote get-url origin 2>/dev/null); then
  echo "byggsteg: $dir has no origin remote to fetch $branch from" >&2
  exit 1
fi
before=$(git rev-parse --short HEAD 2>/dev/null) || before=none
attempt=1
while :; do
  if GIT_TERMINAL_PROMPT=0 git fetch --quiet origin "+refs/heads/$branch:refs/remotes/origin/$branch"; then
    break
  fi
  if [ "$attempt" -ge 3 ]; then
    echo "byggsteg: could not fetch $branch from $origin after 3 attempts; is the branch still there, and is the remote reachable?" >&2
    exit 1
  fi
  echo "byggsteg: fetching $branch from $origin failed; retrying in $((attempt * 2))s" >&2
  sleep $((attempt * 2))
  attempt=$((attempt + 1))
done
if ! git checkout --quiet --force -B "$branch" FETCH_HEAD; then
  echo "byggsteg: fetched $branch from $origin but could not check it out" >&2
  exit 1
fi
after=$(git rev-parse --short HEAD)
if [ "$before" = "$after" ]; then
  echo "byggsteg: $branch was already at $after"
else
  echo "byggsteg: $branch moved from $before to $after"
fi


byggsteg: trunk moved from e3e843a to 61c2204

→ 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 61c220496542b169cef6f29e4e1d349989378b68
Author: Josep Bigorra <jjbigorra@gmail.com>
Date:   Mon Nov 2 23:29:20 2026 +0100

    feat: Improve font configuration

M       README.org
M       lib/lucidplan/config.scm
M       lib/lucidplan/html.scm
M       maak.scm
M       pub/css/fonts.css
M       pub/css/lucidplan-luxe.css
M       pub/css/lucidplan.olive.min.css
M       pub/css/phosphor.css
D       pub/fonts/LSANS.woff
D       pub/fonts/LSANSD.woff
D       pub/fonts/LSANSDI.woff
D       pub/fonts/LSANSI.woff
D       pub/fonts/LTYPE.woff
D       pub/fonts/LTYPEB.woff
D       pub/fonts/LTYPEBO.woff
D       pub/fonts/LTYPEO.woff
D       pub/fonts/gf-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko20yw.woff2
D       pub/fonts/gf-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko40yyygA.woff2
D       pub/fonts/gf-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko50yyygA.woff2
D       pub/fonts/gf-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko70yyygA.woff2
D       pub/fonts/gf-LDIuaomQNQcsA88c7O9yZ4KMCoOg4Koz4y6qhA.woff2
D       pub/fonts/gf-LDIuaomQNQcsA88c7O9yZ4KMCoOg4Koz4yCqhMva.woff2
D       pub/fonts/gf-LDIuaomQNQcsA88c7O9yZ4KMCoOg4Koz4yGqhMva.woff2
D       pub/fonts/gf-LDIuaomQNQcsA88c7O9yZ4KMCoOg4Koz4yOqhMva.woff2
D       pub/fonts/gf-P5sZzZuLY8Lb_G1RikFkwPjBvsk1NTJNJz_8XA.woff2
D       pub/fonts/gf-P5sZzZuLY8Lb_G1RikFkwPjBvsk1NYZsAgLF.woff2
D       pub/fonts/gf-P5sZzZuLY8Lb_G1RikFkwPjBvsk1NYdsAgLF.woff2
D       pub/fonts/gf-P5sZzZuLY8Lb_G1RikFkwPjBvsk1NYhsAg.woff2
D       pub/fonts/gf-P5sbzZuLY8Lb_G1RikFkwPjBvsk-BYp0Bg.woff2
D       pub/fonts/gf-P5sbzZuLY8Lb_G1RikFkwPjBvsk_BYp0Bg.woff2
D       pub/fonts/gf-P5sbzZuLY8Lb_G1RikFkwPjBvskwBYo.woff2
D       pub/fonts/gf-P5sbzZuLY8Lb_G1RikFkwPjBvsmKJK9JPzs.woff2
A       pub/fonts/gf-QGYqz_wNahGAdqQ43Rh_eZDkv_1w4A.woff2
A       pub/fonts/gf-QGYqz_wNahGAdqQ43Rh_eZDlv_1w4A.woff2
A       pub/fonts/gf-QGYqz_wNahGAdqQ43Rh_eZDrv_0.woff2
A       pub/fonts/gf-QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2
A       pub/fonts/gf-QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2
A       pub/fonts/gf-QGYsz_wNahGAdqQ43Rh_fKDp.woff2
A       pub/fonts/gf-vEFI2_tTDB4M7-auWDN0ahZJW1gU8tenXg.woff2
A       pub/fonts/gf-vEFI2_tTDB4M7-auWDN0ahZJW1gV8tenXg.woff2
A       pub/fonts/gf-vEFI2_tTDB4M7-auWDN0ahZJW1gW8tenXg.woff2
A       pub/fonts/gf-vEFI2_tTDB4M7-auWDN0ahZJW1gY8tenXg.woff2
A       pub/fonts/gf-vEFI2_tTDB4M7-auWDN0ahZJW1gb8tc.woff2
A       pub/fonts/gf-vEFI2_tTDB4M7-auWDN0ahZJW1gf8tenXg.woff2
A       pub/fonts/gf-vEFK2_tTDB4M7-auWDN0ahZJW1gewtG_Wo7H.woff2
A       pub/fonts/gf-vEFK2_tTDB4M7-auWDN0ahZJW1gewtW_Wg.woff2
A       pub/fonts/gf-vEFK2_tTDB4M7-auWDN0ahZJW1gewta_Wo7H.woff2
A       pub/fonts/gf-vEFK2_tTDB4M7-auWDN0ahZJW1gewti_Wo7H.woff2
A       pub/fonts/gf-vEFK2_tTDB4M7-auWDN0ahZJW1gewtq_Wo7H.woff2
A       pub/fonts/gf-vEFK2_tTDB4M7-auWDN0ahZJW1gewtu_Wo7H.woff2
A       pub/fonts/juliamono-bold-latin.woff2
A       pub/fonts/juliamono-normal-latin.woff2
D       pub/fonts/l_10646.woff

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

substitute: looking for substitutes on 'https://bordeaux.guix.gnu.org'... 100.0%
20.5 MB will be downloaded
.
waiting for locks or build slots...
.
.
..
substitute: looking for substitutes on 'https://bordeaux.guix.gnu.org'... 100.0%
83.1 MB will be downloaded
.
waiting for locks or build slots...
.....

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

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

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


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


ℹ️ info - shuffled with seed 625537933 (replay with VERITAS_SEED=625537933)
+----------------------------------------------+
[📑 suite] the day a submitted date names
  [🧪 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
  [🧪 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 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
+----------------------------------------------+
[📑 suite] the states a sprint can be in
  [🧪 test] only one state is the one a team is in
    [⚙️ assert] active is running
    ✅ got: #t
    [⚙️ assert] planned is not
    ✅ got: #f
  [🧪 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 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
  [🧪 test] the three states are the shipped ladder
    [⚙️ assert] planned, active, closed
    ✅ got: (planned active closed)
  [🧪 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
+----------------------------------------------+
[📑 suite] how a deadline stands, read against today
  [🧪 test] a day further out is simply scheduled
    [⚙️ assert] the day after the warning
    ✅ got: scheduled
  [🧪 test] a day that has passed is overdue
    [⚙️ assert] yesterday is overdue
    ✅ got: overdue
  [🧪 test] today is today
    [⚙️ assert] the day itself
    ✅ got: due-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 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
  [🧪 test] the warning is a week, and it is the same week everywhere
    [⚙️ assert] the threshold the pages share
    ✅ got: 7
+----------------------------------------------+
[📑 suite] counting the days between two of them
  [🧪 test] a day and its number are each other's
    [⚙️ assert] the number of a day names it back
    ✅ got: 2026-10-27
  [🧪 test] the answer is negative when the second day is earlier
    [⚙️ assert] a week the other way
    ✅ got: -7
  [🧪 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
  [🧪 test] a week is seven days
    [⚙️ assert] seven days
    ✅ got: 7
  [🧪 test] a value that is not a day is no difference
    [⚙️ assert] two things that are not days
    ✅ got: #f
  [🧪 test] the same day is no days apart
    [⚙️ assert] a day and itself
    ✅ got: 0
+----------------------------------------------+
[📑 suite] the line a sprint is burnt down along
  [🧪 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] 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 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] 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] 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] a closed sprint is drawn to its last day
    [⚙️ assert] all five days, read afterwards
    ✅ got: 5
  [🧪 test] a sprint whose end is before its beginning is not drawn
    [⚙️ assert] a backwards sprint draws nothing
    ✅ got: ()
  [🧪 test] a sprint with no dates is not drawn
    [⚙️ assert] no days to draw
    ✅ got: ()
  [🧪 test] a sprint with nothing in it is a flat line at nothing
    [⚙️ assert] every day is zero
    ✅ got: (0 0 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
ℹ️ info - ran 5 tasks in: 0 seconds, 162189 microseconds

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


ℹ️ info - shuffled with seed 625537933 (replay with VERITAS_SEED=625537933)
+----------------------------------------------+
[📑 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] 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 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] 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)
  [🧪 test] an id read back from the database names the same comment
    [⚙️ assert] a numeric parent names the same comment
    ✅ got: 1
  [🧪 test] a comment nothing answers has no children
    [⚙️ assert] the last word is the last word
    ✅ got: ()
+----------------------------------------------+
[📑 suite] how a download is named
  [🧪 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
  [🧪 test] a path in the submitted name never reaches the header
    [⚙️ assert] the plain form is cut to the last name
    ✅ got: hosts
+----------------------------------------------+
[📑 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 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 is cut to a length a header can carry
    [⚙️ assert] a very long name is shortened
    ✅ got: #t
  [🧪 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
  [🧪 test] a name that is just a name is kept
    [⚙️ assert] a plain name survives
    ✅ got: report.pdf
  [🧪 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
+----------------------------------------------+
[📑 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] 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
  [🧪 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
ℹ️ info - ran 5 tasks in: 1 seconds, -907151 microseconds

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


ℹ️ info - shuffled with seed 625537933 (replay with VERITAS_SEED=625537933)
+----------------------------------------------+
[📑 suite] a ticket's story-point estimate
  [🧪 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 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] 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 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] the changes a write to a ticket is recorded with
  [🧪 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 ticket submitted back unchanged is not an edit
    [⚙️ assert] nothing moved, so nothing is recorded
    ✅ got: ()
  [🧪 test] an estimate that was cleared is a change
    [⚙️ assert] the estimate it had is recorded
    ✅ got: (3 )
  [🧪 test] an estimate that was never made is not a change
    [⚙️ assert] no estimate answers no change
    ✅ got: ()
  [🧪 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] 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
+----------------------------------------------+
[📑 suite] an estimate changed from the ticket's own page
  [🧪 test] clearing an estimate is a change the menu offers
    [⚙️ assert] the empty row changes the ticket
    ✅ got: ((estimate-points . ))
  [🧪 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 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 the menu offered is accepted
    [⚙️ assert] the change is the column and its value
    ✅ got: ((estimate-points . 8))
+----------------------------------------------+
[📑 suite] a ticket's t-shirt size
  [🧪 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
  [🧪 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] 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
ℹ️ info - ran 4 tasks in: 0 seconds, 91123 microseconds

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


ℹ️ info - shuffled with seed 625537933 (replay with VERITAS_SEED=625537933)
+----------------------------------------------+
[📑 suite] the deck a table deals
  [🧪 test] a deck is named in the words a reader is shown
    [⚙️ assert] the numbers a team burns down
    ✅ got: story points
    [⚙️ assert] what a first pass is written in
    ✅ got: t-shirt sizes
  [🧪 test] a deck nobody deals is refused
    [⚙️ assert] the fibonacci deck is not one of ours
    ✅ got: #f
    [⚙️ assert] an empty form names no deck
    ✅ got: #f
    [⚙️ assert] something that is not text names no deck
    ✅ got: #f
  [🧪 test] the decks are the two ladders a ticket is estimated on
    [⚙️ assert] the two the section ships
    ✅ got: (points sizes)
    [⚙️ assert] and their sizes are the ticket ladders
    ✅ got: (xs s m l xl xxl)
    [⚙️ assert] and their points are too
    ✅ got: (0 1 2 3 5 8 13 21 34)
  [🧪 test] a deck the table offers is taken as it is stored
    [⚙️ assert] story points
    ✅ got: points
    [⚙️ assert] t-shirt sizes
    ✅ got: sizes
  [🧪 test] a deck typed in capitals means the same
    [⚙️ assert] capitals are the same deck
    ✅ got: points
+----------------------------------------------+
[📑 suite] what a round of cards adds up to
  [🧪 test] a tally of sizes is counted too
    [⚙️ assert] two sizes, in ladder order
    ✅ got: ((s . 1) (l . 1))
  [🧪 test] a round nobody played has no tally
    [⚙️ assert] no cards, no rows
    ✅ got: ()
  [🧪 test] the room agrees on the card most of it played
    [⚙️ assert] the card played most often
    ✅ got: (5)
  [🧪 test] a room that agrees has no spread
    [⚙️ assert] one card is no spread
    ✅ got: #f
    [⚙️ assert] the same card twice is no spread
    ✅ got: #f
    [⚙️ assert] no cards is no spread
    ✅ got: #f
  [🧪 test] a round with no mean has none
    [⚙️ assert] sizes have no mean
    ✅ got: #f
    [⚙️ assert] no cards have no mean
    ✅ got: #f
  [🧪 test] an even split is a split, not a decision
    [⚙️ assert] both cards of a tie, in ladder order
    ✅ got: (2 5)
  [🧪 test] the mean of a round of points is a number
    [⚙️ assert] two cards average where they sit
    ✅ got: 6.5
    [⚙️ assert] one card is its own mean
    ✅ got: 3.0
  [🧪 test] a card nobody played is not a row of the tally
    [⚙️ assert] two cards were played, in ladder order
    ✅ got: ((2 . 1) (5 . 2))
  [🧪 test] a round nobody played agrees on nothing
    [⚙️ assert] no cards, no agreement
    ✅ got: ()
  [🧪 test] a spread is the two ends of the disagreement
    [⚙️ assert] the lowest and the highest of the points
    ✅ got: ((low . 2) (high . 13))
    [⚙️ assert] the ends of the sizes are sizes
    ✅ got: ((low . s) (high . xxl))
+----------------------------------------------+
[📑 suite] what a browser may know about a card before the reveal
  [🧪 test] a card is answered once the room is revealed
    [⚙️ assert] a point, revealed
    ✅ got: 5
    [⚙️ assert] a size, revealed, in capitals
    ✅ got: M
  [🧪 test] a seat that played nothing has nothing to show either way
    [⚙️ assert] no card, revealed
    ✅ got: #f
    [⚙️ assert] no card, unrevealed
    ✅ got: #f
  [🧪 test] a card is not answered while the cards are face down
    [⚙️ assert] a point, not yet revealed
    ✅ got: #f
    [⚙️ assert] a size, not yet revealed
    ✅ got: #f
+----------------------------------------------+
[📑 suite] a card of one of the decks
  [🧪 test] the cards are dealt in the ladder's own order
    [⚙️ assert] a hand of points opens on nothing
    ✅ got: (0 1 2 3 5 8 13 21 34)
    [⚙️ assert] a hand of sizes opens on the smallest
    ✅ got: (xs s m l xl xxl)
    [⚙️ assert] a deck nobody deals deals nothing
    ✅ got: ()
  [🧪 test] a card nobody deals is refused
    [⚙️ assert] a number off the ladder
    ✅ got: #f
    [⚙️ assert] a size off the ladder
    ✅ got: #f
    [⚙️ assert] a card of the other deck
    ✅ got: #f
    [⚙️ assert] an empty form plays nothing
    ✅ got: #f
    [⚙️ assert] nothing at all plays nothing
    ✅ got: #f
  [🧪 test] the sizes sort by the ladder and not by their letters
    [⚙️ assert] the smallest is the first rung
    ✅ got: 0
    [⚙️ assert] the largest is the last
    ✅ got: 5
    [⚙️ assert] a card nobody deals has no rung
    ✅ got: #f
  [🧪 test] a card of the room's deck is taken as the deck spells it
    [⚙️ assert] a story point
    ✅ got: 5
    [⚙️ assert] a t-shirt size is lowercased
    ✅ got: m
    [⚙️ assert] space around a card is not part of it
    ✅ got: 8
  [🧪 test] a card is shown with the face the ladder gives it
    [⚙️ assert] a size is shown in capitals
    ✅ got: XXL
    [⚙️ assert] a number is already what it says
    ✅ got: 13
+----------------------------------------------+
[📑 suite] the name a participant is known by
  [🧪 test] a name is capped because it is a label on a card
    [⚙️ assert] a name at the cap is a name
    ✅ got: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    [⚙️ assert] one character past it is not
    ✅ got: #f
  [🧪 test] a run of spaces inside a name is one space
    [⚙️ assert] the words are kept, the gap is not
    ✅ got: joe the dev
  [🧪 test] a name of nothing is no name
    [⚙️ assert] an empty form
    ✅ got: #f
    [⚙️ assert] a form of spaces
    ✅ got: #f
    [⚙️ assert] nothing at all
    ✅ got: #f
    [⚙️ assert] something that is not text
    ✅ got: #f
  [🧪 test] the space around a name is not part of it
    [⚙️ assert] a padded name is trimmed
    ✅ got: joe
+----------------------------------------------+
[📑 suite] the ticket a table is about
  [🧪 test] a table opened for a ticket answers which one
    [⚙️ assert] the project and the number in it
    ✅ got: ((project . lucidplan) (number . 479))
  [🧪 test] a table opened for nothing is about no ticket
    [⚙️ assert] a room with no ticket columns at all
    ✅ got: #f
    [⚙️ assert] a project and no number
    ✅ got: #f
    [⚙️ assert] a number and no project
    ✅ got: #f
+----------------------------------------------+
[📑 suite] the seat a browser holds without an account
  [🧪 test] sitting at a second table does not cost the first seat
    [⚙️ assert] the newer seat is written first
    ✅ got: room-b~secret-2|room-a~secret-1
    [⚙️ assert] a seat taken again moves to the front
    ✅ got: room-a~secret-9|room-b~secret-2
  [🧪 test] the number of seats a cookie holds is capped
    [⚙️ assert] the cap is what the section says
    ✅ got: 8
    [⚙️ assert] the newest seat is the one that is there
    ✅ got: secret-new
  [🧪 test] a value that is not a seat is not a seat
    [⚙️ assert] no cookie at all
    ✅ got: ()
    [⚙️ assert] an empty cookie
    ✅ got: ()
    [⚙️ assert] an entry without a secret
    ✅ got: ()
    [⚙️ assert] an entry with an empty room
    ✅ got: ()
    [⚙️ assert] a malformed entry is skipped, not obeyed
    ✅ got: ((room-b . secret-2))
  [🧪 test] a seat is a room and a secret
    [⚙️ assert] one seat reads back
    ✅ got: ((room-a . secret-1))
    [⚙️ assert] two seats read back in written order
    ✅ got: ((room-a . secret-1) (room-b . secret-2))
  [🧪 test] a seat is found by the room it was taken at
    [⚙️ assert] the seat at one room
    ✅ got: secret-2
    [⚙️ assert] no seat at a room nobody sat at
    ✅ got: #f
+----------------------------------------------+
[📑 suite] what a table may write onto a ticket
  [🧪 test] an agreed card is a change a ticket would accept
    [⚙️ assert] the agreed points are a submitable change
    ✅ got: ((estimate-points . 8))
    [⚙️ assert] and the agreed size is one too
    ✅ got: ((estimate-size . xl))
  [🧪 test] a round nobody played has nothing to write down
    [⚙️ assert] no cards, no number
    ✅ got: #f
  [🧪 test] a room that split has nothing to write down
    [⚙️ assert] an even split takes no number
    ✅ got: #f
    [⚙️ assert] and the tally still names both
    ✅ got: (2 5)
  [🧪 test] the deck a table deals is the column a ticket carries
    [⚙️ assert] points are the ticket's points
    ✅ got: estimate-points
    [⚙️ assert] sizes are the ticket's size
    ✅ got: estimate-size
    [⚙️ assert] a deck with no column has none
    ✅ got: #f
  [🧪 test] a card the ladder does not hold is not a change
    [⚙️ assert] a number the team does not estimate with
    ✅ got: #f
    [⚙️ assert] a size nobody sizes at
    ✅ got: #f
  [🧪 test] a room that agreed on one card has a number to take
    [⚙️ assert] the card everybody played
    ✅ got: 5
    [⚙️ assert] the card most of it played
    ✅ got: 5
    [⚙️ assert] a size is a card too
    ✅ got: m
ℹ️ info - ran 8 tasks in: 0 seconds, 195892 microseconds

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


ℹ️ info - shuffled with seed 625537933 (replay with VERITAS_SEED=625537933)
+----------------------------------------------+
[📑 suite] a duration, read the ways it is written
  [🧪 test] space around the whole thing is not part of it
    [⚙️ assert] a padded form still means its duration
    ✅ got: 30
  [🧪 test] the case of what was typed does not matter
    [⚙️ assert] capitals are the same duration
    ✅ got: 90
  [🧪 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] 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
+----------------------------------------------+
[📑 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
    [⚙️ 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] a number of minutes, said to a reader
  [🧪 test] hours and minutes are said together
    [⚙️ assert] an hour and a half
    ✅ got: 1h 30m
  [🧪 test] less than an hour is said in minutes
    [⚙️ assert] three quarters of an hour
    ✅ got: 45m
  [🧪 test] nothing is said as nothing rather than as empty space
    [⚙️ assert] zero minutes still reads as a duration
    ✅ got: 0m
  [🧪 test] a whole number of hours is said in hours
    [⚙️ assert] two hours
    ✅ got: 2h
  [🧪 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: 
+----------------------------------------------+
[📑 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] 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] 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] 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] what a ticket's time entries add up to
  [🧪 test] a ticket nobody logged time on adds up to nothing
    [⚙️ assert] no entries is no minutes
    ✅ got: 0
  [🧪 test] a row that is not a duration does not break the sum
    [⚙️ assert] a malformed row counts as nothing
    ✅ got: 120
  [🧪 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] every entry counts toward the total
    [⚙️ assert] two and a half hours over three days
    ✅ got: 165
ℹ️ info - ran 5 tasks in: 0 seconds, 120734 microseconds

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


ℹ️ info - shuffled with seed 625537933 (replay with VERITAS_SEED=625537933)
+----------------------------------------------+
[📑 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] 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
  [🧪 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] 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] the values are read in the order they were written
    [⚙️ assert] the read-back values keep their order
    ✅ got: (todo done rejected)
+----------------------------------------------+
[📑 suite] the assignee a form submitted
  [🧪 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
  [🧪 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
+----------------------------------------------+
[📑 suite] the list a project's tickets move through
  [🧪 test] a project with a list of its own uses it
    [⚙️ assert] the project's own list is the one read
    ✅ got: (todo shipped)
  [🧪 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 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))
+----------------------------------------------+
[📑 suite] a status that is not on the list
  [🧪 test] is still a ticket somebody is working on
    [⚙️ assert] an unknown status is bucketed as open
    ✅ got: open
  [🧪 test] is not in any bucket's list
    [⚙️ assert] the done bucket holds the statuses that are done
    ✅ got: (done)
  [🧪 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
+----------------------------------------------+
[📑 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] an inline change from the ticket's own page
  [🧪 test] a status the project does not move through is refused
    [⚙️ assert] a status invented by hand changes nothing
    ✅ got: #f
  [🧪 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] with no project list the instance's own is the ladder
    [⚙️ assert] the first status of the instance's list is accepted
    ✅ got: #t
ℹ️ info - ran 6 tasks in: 0 seconds, 109364 microseconds

finished running tests
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling test/veritas/unit/burndown-spec.scm
;;; compiling lib/lucidplan/domain.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/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-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
;;; 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/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/planning-poker-spec.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/lucidplan/trunk/test/veritas/unit/planning-poker-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
;;; 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
;;; note: source file /etc/byggsteg/job-clone/lucidplan/trunk/maak.scm
;;;       newer than compiled /root/.cache/guile/ccache/3.0-LE-8-4.6/etc/byggsteg/job-clone/lucidplan/trunk/maak.scm.go
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /etc/byggsteg/job-clone/lucidplan/trunk/maak.scm
;;; compiled /root/.cache/guile/ccache/3.0-LE-8-4.6/etc/byggsteg/job-clone/lucidplan/trunk/maak.scm.go


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

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 && maak migrate

-------------------------------------------------
Starting Mutastructura: guile -L /gnu/store/dlg9fm51pz31zv0fbqq5svy7sh0dbs2h-profile/share/guile/site/3.0 -c ((@(mutastructura main) main)) --database sqlite3 --connection lucidplan.db --migrations /etc/byggsteg/job-clone/lucidplan/trunk/resources/migrations/sqlite --resources /gnu/store/dlg9fm51pz31zv0fbqq5svy7sh0dbs2h-profile/share/resources

---------------------------------------------------------
MUTASTRUCTURA: Schema Migrations tooling powered by Lisp

 🏛️  M U T A S T R U C T U R A  🏛️

 _ _ V E N I • V I D I • L I S P _ _


WARNING: Checksum mismatch for already applied migration 2026-04-01_10:52:00_init_lucidplan_schema.sql!

WARNING: Checksum mismatch for already applied migration 2026-05-22_11:21:00_drop_unique_constraint_human_id.sql!

WARNING: Checksum mismatch for already applied migration 2026-10-14_12:00:00_add_project_ticket_statuses.sql!

WARNING: Checksum mismatch for already applied migration 2026-10-22_12:00:00_add_ticket_estimates_collaboration_history.sql!

WARNING: Checksum mismatch for already applied migration 2026-10-27_12:00:00_add_sprints_milestones_time_entries.sql!

WARNING: Checksum mismatch for already applied migration 2026-10-27_13:00:00_add_planning_poker.sql!

WARNING: Checksum mismatch for already applied migration 2026-10-27_14:00:00_add_poker_item_ticket.sql!

WARNING: Checksum mismatch for already applied migration 2026-10-27_15:00:00_add_poker_room_ticket.sql!

FOUND MIGRATION HISTORY:

((id . 1) (name . "mutastructura-init.sql") (checksum . "9cc74e39062f44295eed10dcc318b67a87a2dc0ac9bc62eb76500fc4f3650e03") (applied-at . "2026-04-07 13:04:36"))

((id . 2) (name . "2026-04-01_10:52:00_init_lucidplan_schema.sql") (checksum . "f3923079248f1ffca1e9b06357e0a2207f07179609cbe24c44ab1108ed512593") (applied-at . "2026-04-07 13:11:37"))

((id . 3) (name . "2026-04-07_12:21:00_add_root_user.sql") (checksum . "7b09084eda456e679e753b07db590033604f1a92612160ab84f621405218b9b3") (applied-at . "2026-04-07 13:11:37"))

((id . 4) (name . "2026-05-22_11:21:00_drop_unique_constraint_human_id.sql") (checksum . "1ec83c352cd035fa547cc2f901ae01ab9dbb5c1cacb33d6fee2c683fa7f21e61") (applied-at . "2026-05-22 22:02:32"))

((id . 5) (name . "2026-10-14_12:00:00_add_project_ticket_statuses.sql") (checksum . "e3f1e0272a130ea622a137f106dbbd5986e85ae3732cfab84710f0e3cf53d867") (applied-at . "2026-10-14 11:52:12"))

((id . 6) (name . "2026-10-22_12:00:00_add_ticket_estimates_collaboration_history.sql") (checksum . "31de462a20b09362e9600ea0095f25285a87edf886bf29ef314b337014788dd3") (applied-at . "2026-10-22 18:30:25"))

((id . 7) (name . "2026-10-27_12:00:00_add_sprints_milestones_time_entries.sql") (checksum . "3e3c2a32f6c6871b78f74a8481f1c9bfeba95da302a9eab5f033b84cf5a33a4e") (applied-at . "2026-10-26 08:10:30"))

((id . 8) (name . "2026-10-27_13:00:00_add_planning_poker.sql") (checksum . "a3e14ee805b11306e176d4ec5c746b057633b8704034628afe0fdb749f01a1e5") (applied-at . "2026-10-27 20:24:22"))

((id . 9) (name . "2026-10-27_14:00:00_add_poker_item_ticket.sql") (checksum . "7b97f73c573ae9b4fc27d99429ef29bc5eb8e24790dd9a655543d9609a77d50f") (applied-at . "2026-10-27 20:24:22"))

((id . 10) (name . "2026-10-27_15:00:00_add_poker_room_ticket.sql") (checksum . "0b37bad93d5d9a4884765af4cf4e703df6991b5d207e0e88174398f9abb13792") (applied-at . "2026-10-27 20:24:22"))

((id . 11) (name . "2026-10-28_17:00:00_add_project_avatar.sql") (checksum . "80d4091f0ba7bc3be6678303f272f79bed9aa246ff5a4f2d535e772aa2764b27") (applied-at . "2026-10-28 19:33:34"))

((id . 12) (name . "2026-10-28_18:00:00_store_project_avatar_in_database.sql") (checksum . "6bbca6c6b86fcdbd1af22312c7f5da0f84d3d615c3389f3652420b765450b1df") (applied-at . "2026-10-28 19:33:34"))

((id . 13) (name . "2026-10-28_19:00:00_store_ticket_attachments_in_database.sql") (checksum . "287f4b621cac3766d5daa1ee2d9ea00668071a08c16e2c540f1b48ae60f7231a") (applied-at . "2026-10-28 19:33:34"))

((id . 14) (name . "2026-10-28_20:00:00_add_epics.sql") (checksum . "f5b3f8f655bdda335fed6ed4164917328a0f3911d8fa0a57caae082c4350d778") (applied-at . "2026-10-28 20:19:04"))

((id . 15) (name . "2026-10-28_21:00:00_add_labels_and_saved_filters.sql") (checksum . "0ab2d2f9dc0a0b2424907b378f69f6576959a210258f92f8100dc488f8851c93") (applied-at . "2026-10-28 20:19:04"))


---------------------------------------------------------
NOTHING TO DO!

CLOSING DATABASE...



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

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: migrate


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

=> Running Maak task: migrate

  Bring the database up to the schema the migrations describe.

Every migration in the migrations directory that the ledger has not recorded
yet is applied, in filename order, each inside its own transaction; a
database that is already up to date is left alone, and one that is not is
never half-migrated, because a migration that fails takes its own statements
back with it.  The ledger is what makes that possible -- it is the record of
what has run, and mutastructura is the only thing that writes it -- so the
migrations are applied here and nowhere else, by hand or otherwise.

Executing: guix time-machine --channels=channels.scm -- shell -m manifest.scm -- mutastructura --database sqlite3 --connection lucidplan.db --migrations ./resources/migrations/sqlite

→ 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'

→ 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.
inspect the job's lambda expression
(lambda()
  `((task . ("maak-test" "maak-migrate" "maak-deploy" "pull-and-restart"))
    (daemon-name . "lucidplan-daemon")
    (project . "lucidplan")
    (branch-name . "trunk")
    (clone-url . "https://github.com/jjba23/lucidplan.git")))