Skip to content

Recurring Task Evidence Design

Ed Mozley edited this page Oct 19, 2026 · 6 revisions

Recurring task evidence - design (#181)

Status: built (19 Oct 2026), in the next release. This page sets out what I am building for issue #181, and why. It was written before any code so the approach could be checked first. The user guide is Repeating task evidence.

Updated 18 Oct 2026: the six design decisions are made - see section 7. Two of them became settings, and running the report is its own permission.

Updated 19 Oct 2026: @Dr4inder read the design and asked for three things to be made explicit. All three are now in it - see section 9. In short: a moved due date stays visible, and a later edit cannot turn a late completion into "on time"; a successor that a completion made due, but that never appeared, shows as Missing in a repeat set to after completion too; and a removed occurrence keeps its whole record, still reachable after the series or its checklist is gone.

The request, from issue #181 (raised by @Dr4inder):

"For internal reviews and external audits, individual completed tasks do not provide the complete picture. Users also need to establish which executions were expected, whether they occurred on time, who performed the work, what results were recorded and which exceptions remain unresolved."

In plain words

It is about repeating tasks in the Tasks module, and the checklists that can be attached to them.

Take a task "Check the backups restore", set to repeat on the 1st of every month, with a checklist: restore a test file, compare the checksum, record the result. Every month FreeITSM creates a new copy, somebody works through the checklist and marks it done.

An auditor then asks: "Show me this check was done every month this year."

Today the answer is to open twelve tasks one by one - and even then you cannot see that March was never created, that June was done ten days late, that in August the task was ticked done although the restore failed, or that somebody edited the checklist in September.

The request is for one page per repeating task that lists every occurrence that should have happened in a period, what actually happened to each, the gaps and the exceptions - printable to PDF and exportable as CSV, and never rewritten by later changes to the series or its checklist.


1. The principle: record first, then report

A report can only show what was recorded. So most of this work is quiet record-keeping underneath the tasks that already exist - who completed a task, when it was reopened, what each checklist step said - and only then a report that reads it.

Two consequences, stated up front:

  • History starts on the day this ships. Some of what an auditor wants was never recorded (section 2). The report will say "not recorded" for it rather than guess - which is exactly what the issue asks: "Historical information that was never recorded should be marked as unavailable rather than reconstructed as fact."
  • Recording should ship first and on its own, before the report, so that the history is already accumulating by the time the report exists (section 6).

Nothing about how anybody uses Tasks today changes unless they choose it. The one behaviour change that is not invisible - what happens when you delete an occurrence, or untick a step on a finished task - is a decision for section 7.


2. What FreeITSM records today

Mapped from the code (3.9.0), not from memory:

The report needs Today Verdict
Every occurrence in a series tasks.recurrence_id links them βœ… Have it
When each was created created_datetime (UTC) βœ… Have it - shows late catch-up
The date it was scheduled for Only due_date, which can be edited afterwards, and the original is not kept ⚠️ From now on
When it was completed completed_datetime - but cleared if the task is reopened ⚠️ Partly
Who completed it Not recorded anywhere ❌ From now on
Status changes, reopening, due-date and assignee changes Workflow events fire, but nothing is kept ❌ From now on
Checklist step results, who and when Each step keeps who ticked it and when - until it is unticked, which wipes all three ⚠️ Current state only
Checklist template as it was Saving a template deletes its steps and writes them again; old versions survive only in the copies on tasks ❌ From now on
The repeat rule as it was Overwritten in place; who stopped or changed it is not kept ❌ From now on
A skipped occurrence No such thing: the nearest is the Cancelled status ❌ New
A deleted occurrence Hard-deleted with its comments, time, checklists and history ❌ From now on
Comments, time, attached documents Kept, with author and time βœ… Have it
A task's existing history (task_audit) Written in three places, shown nowhere βœ… Have it, unread

Can never be reconstructed for the past: who completed any existing task; earlier completion times of reopened tasks; status, due-date and assignee changes made in the app; previous versions of rules and checklist templates; deleted occurrences. The report labels these honestly.


3. The proposal, in seven parts

Part A - A task keeps its history

A new append-only task_events table: one row per thing that happened to a task, with who (an analyst, or the system for the scheduler), when (UTC), what, and the before and after values. Rows are never edited or deleted. Written inside the same database transaction as the change itself, in the one place every change already goes through (TasksService), so the board, the task window, the calendar, the REST API and the scheduler cannot disagree.

Events recorded:

Event Detail kept
Created by a person, or by the repeat: after completion, on schedule, or catch-up - and, once #182 is built, which run of the repeating-tasks job created it
Status changed from / to, and whether that means completed or reopened
Completed by whom - the column that does not exist today - and the due date in force at that moment, so a later edit to the due date cannot change how the completion is judged
Reopened the completion it undid is kept, not erased
Due date / start date changed from / to
Assigned (analyst or team) from / to
Skipped reason (Part D)
Checklist attached / removed which checklist, which template version
Evidence exported Part F

The existing task_audit rows (company moves, checklist overrides, calendar-sync changes) are shown alongside, so nothing already recorded is lost.

Also new on each occurrence: scheduled_date - the date the repeat created it for, set once and never edited. due_date stays editable as it is today. This is what lets the report tell "scheduled for 1 March, moved to 8 March, done on 7 March" from "due 8 March, done 7 March".

And previous_occurrence_id - in a repeat set to after completion, the occurrence whose completion (or skip) created this one. It links each occurrence to the one before, which is what lets the report see a successor that should exist and does not (section 9), and what stops reopening and completing a task again from creating a second successor (section 5).

When a completion or a skip makes a successor due, that is recorded on the completed or skipped event before the successor is created: whether the rule was active, whether it had ended, and the date it should be due. So if creating it then fails, the expectation is already on record.

A History tab on the task window shows these events. That also fixes the help text, which already promises that some changes are "recorded in the task's history" - a history nothing can currently display.

Part B - A repeat keeps its rule history

Each save of a repeat writes a version of the rule (a JSON snapshot, who, when), and stopping, resuming and finishing are events with a cause: stopped by a person, resumed by a person, or finished automatically (end date or count reached).

That is what lets the report work out which dates were expected under the rule that applied at the time, and show a stopped period as stopped, not as missing.

There is no separate "pause" today - stopping and later saving the repeat again is the pause. I propose keeping that, and simply recording it.

Part C - Checklist results become evidence

  1. Step history. Every tick, untick and recorded value is a row in a new append-only step-history table: who, when, before and after. Unticking no longer erases who ticked it - the history keeps it.
  2. Results, not just ticks (optional per step). A template step can ask for a result: Pass, Fail, Not applicable - and Fail asks for a short exception note. This is the issue's point that "completing a task should not imply that the underlying control had a successful result": the task can be done while a step records a failed restore, and the report shows it as an exception. Steps that do not ask for a result keep working exactly as now.
  3. Template versions. Saving a template keeps the previous version instead of overwriting it, and each checklist on a task records which version it was copied from. The report shows the procedure as it stood for that occurrence.
  4. After a task is complete, its checklist is evidence. What happens when somebody changes a step afterwards is a setting (section 7): by default it is allowed and recorded as a post-completion change, flagged in the report; or it can be refused until the task is reopened.

Part D - Skipping and removing occurrences

  • Skip - a new action on an occurrence of a repeat: "Skip this one", with a reason. It closes the occurrence as skipped (recorded as such, not just Cancelled), and in a repeat set to after completion it still creates the next one, as completing does today. The report shows it as skipped, with the reason - distinct from late, missing or done.
  • Deleting an occurrence must not make it vanish from the record (acceptance criterion 7). What deleting does is a setting (section 7): by default it is allowed, and leaves a record shown in the report as Removed; or occurrences of a repeat cannot be deleted and must be skipped instead.
  • What the record of a removed occurrence keeps (section 9): not just its number, dates and who deleted it, but a frozen copy of everything the report reads - the task as it stood, each checklist with its steps, ticks, values, results and exception notes, the step history, the task's history, its comments, its subtasks and references to its attachments. The history rows themselves are never deleted with the task either. The record belongs to the series, not to the task, so it is still there after the series is stopped or its tasks are gone, and after the checklist template is deleted.

Part E - The series report

Opened from a repeating task (the repeat section of the task window: Execution history), for a chosen period of scheduled dates. It can also be opened from Tasks β†’ Evidence, a list of every repeating series - including stopped ones and ones whose tasks have all been deleted - because a report that can only be reached from a live task disappears with the task (section 9).

One row per expected occurrence, each with: its original due date (the scheduled date); the due date in force when it was completed, and its current due date where those differ; the task's number and current status; when it was created and when completed (local time, with UTC in the export); who it was assigned to and who completed it; the checklist results and exceptions; and an outcome:

Outcome Means
On time completed on or before its original due date - and on or before any revised one
On time after rescheduling completed after its original due date, but on or before the due date it had been moved to when it was completed
Late completed after the due date in force at the time - late even against a revised date
Overdue not completed, and its due date has passed
Open not completed, and not yet due
Skipped skipped, with the reason
Missing expected, but never created (both kinds of repeat - see below)
Removed created, then deleted (Part D)
Not recorded the history needed predates this feature

And flags beside the outcome, none of which can make work look on time:

Flag Means
Rescheduled its due date was moved from the scheduled date before it was completed (from / to, by whom, when)
Due date changed after completion the due date was edited after the task was completed. The outcome is still judged against the date in force at completion.
Created late the scheduler created it after its start date (a catch-up)
Changed after completion a checklist step was changed after the task was completed (Part C)

"On time" compares the completion time in the install's time zone with the due date, because due dates are plain calendar days - a task completed at 00:30 on the 2nd in London was not completed on the 1st.

The two kinds of repeat stay distinct, as the issue asks:

  • On a fixed schedule: expected dates come from the rule (and its versions), so a date with no occurrence is missing. Working this out creates nothing - viewing or exporting the report never generates a task (criterion 2). A date that is due to be created today, and is not yet, shows as Not created yet, not Missing.
  • After completion: the next date only exists once the previous one is done, so there is no calendar to be missing from, and the report never invents a row before the previous occurrence is completed. But once a completion or a skip has made a successor due - the rule was active and had not reached its end date or count, which is recorded at that moment (Part A) - a successor that was never created is shown as Missing, with the date it should have been due. The report also shows the gap between one completion and the next.

A summary line at the top: expected 12, on time 8, on time after rescheduling 1, late 1, skipped 1, missing 1, exceptions 2.

Why an occurrence was missing or created late. With the run history from background job health (#182), a Missing or Created late row also says what the run history shows for that period. The wording claims only what the records establish (section 9):

The records show The report says
no run history at all for that period (before recording began, or older than it is kept) Run history not available for this period
run history exists, but no run of the repeating-tasks job in that period No recorded run of the repeating-tasks job between 2 and 9 March
runs in that period, and they failed The repeating-tasks job failed on 3 March, with its message
a successful run that should have created it The job ran on 3 March but did not create it
a catch-up run created it Created on 9 March by a catch-up run

It never says "the job did not run": an absence of records is not proof that the scheduler stopped.

Part F - Evidence and export

  • Print - a server-rendered page built the same way as Change Management's Export evidence (3.8.0): the series and its rule as it stood, the summary, every occurrence with its checklist results and history, and references to attachments (name, size, who and when, and the file's sha256), comments and linked tickets, changes and follow-up tasks. Times are labelled with the time zone; the page states the period, the time zone and when it was produced, and the browser's Print β†’ Save as PDF gives the PDF.
  • CSV - three files with stable ids that join: occurrences (one row per expected occurrence, with series id, task id and outcome), checklist results (one row per step per occurrence, with task id), and evidence (one row per attachment, comment or link, with task id). UTF-8 with a BOM so Excel reads it, as the other exports do.
  • Every export is recorded on the series' history - who, when, which period - as Change Management's evidence export is.
  • Running the report and the exports is its own permission (Part G). On top of it, the viewer's access applies to both: an occurrence in a company they cannot see is not listed, and a linked ticket they cannot open is named by number only.

Part G - Permissions: Read only, and a permission to run the report

Two pieces, which together make an Auditor role possible - somebody who can read the record and run these reports, and nothing else:

  1. Read only for Tasks. Tasks gains the Read only module access that Change Management and Forms received in 3.8.0 (#180): a read-only user can open Tasks and read every task and its history, and change nothing. Every write in Tasks and in task checklists refuses them, enforced in the services as well as on the screens, with the same coverage test that keeps Change Management honest.
  2. A new permission: "Run evidence reports and exports" (Tasks). It is an everyday-work permission granted through System β†’ Roles, like Choose approvers in Change Management. As with those, an install chooses whether it is required: left unrestricted (the default, so nothing changes on upgrade), anyone who can see a repeating task can run its report; restricted, only people whose role grants it can.

The Auditor recipe: a team with Read only access to Tasks, and a role holding only Run evidence reports and exports. Its members can open any repeating task their companies allow, read its history, and print or export the evidence - and cannot create, edit, complete, skip or delete anything.


4. The issue's acceptance criteria

# Criterion Where
1 Open the history from a series, choose a period by scheduled date Part E
2 An expected but never-created occurrence is a gap; viewing creates nothing Part E (fixed schedule)
3 Late creation and late completion are distinguishable; completion-based repeats keep their semantics Parts A, E
4 Rule changes, pauses and template edits preserve earlier occurrences Parts B, C
5 Checklist results show who and when; missing attribution is labelled Parts C, E (not recorded)
6 Authorised users export occurrences, results, exceptions and evidence Parts F, G (its own permission)
7 Archived, cancelled or removed occurrences do not disappear Parts D, E

5. Other things I found while mapping it

Not part of the request, but they would undermine the report, so they are fixed first:

  • Reopening and completing a task again creates a second next occurrence in a repeat set to after completion. Only one should exist.
  • Dragging a card into a closed column on the board does not create the next occurrence of a repeat set to after completion. The board's drag goes through its own endpoint, which never calls the repeat - so a repeat completed by ticking works and the same repeat completed by dragging silently stops. (Found 19 Oct while answering section 9's second point - it is exactly the gap that point is about.)
  • An occurrence the scheduler creates says it was created by the person who created the original task. It should say it was created by the repeat.
  • The repeat counts "today" in UTC rather than in the install's time zone, so completing a task just after midnight in summer can date the next one from the day before.
  • Deleting a task leaves its document links behind, pointing at nothing.
  • The repeat's preview only knows about occurrences whose due date was never moved.
  • The REST API's notes for Tasks say tasks have no company and no history; both exist.

6. The order to build it in

Each phase is useful on its own and can ship in its own release:

  1. Record - Part A (history and scheduled_date), Part B (rule versions) and the fixes in section 5. Nothing visible except the History tab. Ships first, so history starts accumulating.
  2. Checklist evidence - Part C: step history, optional results and exceptions, template versions, and the after-completion setting.
  3. Skip and remove - Part D, with the deleting setting.
  4. The report and exports - Parts E and F, with the Run evidence reports and exports permission. The run recorder from #182 lands first, so the report can explain missing and late occurrences.
  5. Read only - Part G.

7. Decisions

Made on 18 Oct 2026.

# Question Decision
1 After a task is complete, may its checklist still be changed? A setting in Tasks β†’ Settings: Allowed, and recorded (the default - a typo can be corrected, and the change is flagged in the report) or Refused until the task is reopened.
2 Deleting an occurrence of a repeat A setting in Tasks β†’ Settings: Allowed, and kept as Removed (the default - a record of the occurrence stays) or Not allowed - skip it instead.
3 Step results (Pass / Fail / Not applicable) Opt-in per template step, so every existing checklist keeps working unchanged.
4 Who may run the report and exports A new permission, "Run evidence reports and exports", so an install can have an Auditor role that does nothing else (Part G). Unrestricted by default, so nothing changes on upgrade.
5 Where the report lives From the task (the repeat section), as the issue asks - and from Tasks β†’ Evidence, a list of every series, because a removed series must stay reachable (updated 19 Oct, section 9).
6 History for ordinary, non-repeating tasks Yes - Part A records it for every task; the History tab and the record are the same work either way.

The two settings

Both live on a new Evidence tab in Tasks β†’ Settings, guarded by its own settings permission like the other tabs, and both default to the more forgiving choice so an upgrade changes nothing:

Setting Options Default
Changing a completed task's checklist Allowed, and recorded Β· Refused until the task is reopened Allowed, and recorded
Deleting an occurrence of a repeat Allowed, and kept as Removed Β· Not allowed - skip it instead Allowed, and kept as Removed

Either way the record is never lost: the stricter options just stop the change happening at all. In a multi-company install they are set once for the whole install, like the rest of Tasks' settings.


Which status a skipped occurrence moves to

Skipping closes an occurrence, so it needs a closed status. Status names are yours to choose, so FreeITSM cannot assume one is called Cancelled: the Evidence tab has a third setting, Status a skipped occurrence moves to, a list of your closed statuses. It defaults to the last closed status in your order - Cancelled on a standard install. Whatever it is called, the report knows the occurrence was skipped from the record, not from the status name.


8. Deliberately out of scope

As the issue itself says: no approval chains on tasks, no separate compliance module, and nothing that certifies a control was effective - the report shows what was recorded, and an exception is shown as an exception. Report Packs blocks for this report are not part of it.


9. Three clarifications (19 Oct 2026)

@Dr4inder read the design and asked for three details to be made explicit. Each one changed the design, and each is answered in the part it belongs to.

# Asked for What changed Where
1 Changed deadlines. Timeliness against the original due date must be told apart from timeliness against a revised one. A check due 1 March, moved to 8 March and done on 7 March must stay recognisably rescheduled, and a later due-date edit must not silently turn a late completion into an unqualified On time. Every completion records the due date in force at that moment. The outcome is judged against both the original (scheduled) date and that date: On time, On time after rescheduling, or Late. A Rescheduled flag names the move, and Due date changed after completion flags a later edit - which never changes the outcome. The summary counts on time after rescheduling separately, and the CSV carries all three dates. Parts A and E
2 After-completion repeats. No invented rows before the previous task is completed - but once a completion or skip has made a successor expected under an active rule within its end conditions, a successor that was never created must still be visible. The expectation is recorded on the completed or skipped event before the successor is created, and each successor links back to the occurrence that made it due. A completion that expected a successor and has none is a Missing row. Two bugs that cause exactly this gap are fixed first: dragging a card to a closed column never created the successor, and reopening then completing created two. Parts A and E, section 5
3 Removed occurrences. The retained record must keep the checklist content, results, exceptions and event history the report needs, not only the number, dates and deletion details - and stay accessible if the series or an attached checklist is removed. Deleting an occurrence freezes a full copy of what the report reads into a record owned by the series. History rows are never deleted with a task. Rule versions and checklist template versions outlive the series and the template. The report can be opened from Tasks β†’ Evidence, which lists every series, so it does not depend on a live task. Parts D and E, decision 5

The same reply asked one thing of #182: that the link between the two say "no recorded run in this period" unless the records establish more. Part E's table above is that wording.


Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally