Skip to content

Task Work View Design

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

Task work view - design (#190)

Status: all five phases built (20 Oct 2026), in the next release - forms belong to a task occurrence (Forms on tasks), and the Work view brings instructions, Knowledge procedures, checklists and forms together. Developer guide: Forms on tasks and the Work view. Note: the inline form is the Forms page in a frame rather than an extracted FormFiller - see the developer guide for why.

Decided 19 Oct 2026. This page sets out what I am building for issue #190, and why. It was written before any code so the approach could be checked first.

Updated 19 Oct 2026: @Dr4inder read the design and answered the seven questions - see section 8. Five went as suggested. Two changed the design: reading a task's form answers is now its own permission rather than following from reading the task (section 3), and the procedure links to Knowledge articles, with the text each occurrence was done against frozen into its record (section 3a). They also asked that two people saving the same occurrence's form conflict visibly rather than overwrite each other (Saving progress).

It builds directly on #181 - repeating task evidence, which is built and ships in 3.12.0. #181 is the record and #190 is the workbench: nothing here adds a second place to keep results, a second history or a second completion check. Section 2 says exactly what is reused.

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

"When carrying out a task, its instructions, checklist, form answers and supporting documents are the working material. An analyst should be able to open the task and complete that work in one place."

In plain words

Take a task "Daily backup verification" that repeats every day, weekends included. Each day the analyst reads the backup reports, marks every job OK, Warning or Failure, explains anything that is not OK, attaches the report and raises a follow-up where something needs fixing.

Today that means leaving the task to fill in a form in the Forms module, finding the saved submission, coming back, typing its number into a checklist step, and then completing the task separately. FreeITSM never knows that the submission and the task belong together. The analyst is the only link.

The proposal is:

  1. A task (or its repeat) can say which forms it needs. Each occurrence gets its own fresh copy of that requirement.
  2. The form is filled in inside the task. The submission is linked to that occurrence automatically, so no numbers are copied by hand.
  3. A Work tab in the large task window brings together the instructions, checklists, forms, documents and follow-ups, with progress and one Complete button.
  4. Completing checks the required work through the gate that already exists, so it holds on every path: the task window, the board, the right-click menu, subtasks and the REST API.
  5. All of it feeds the #181 evidence report. Form answers, follow-ups and exceptions appear against the right occurrence and survive in its frozen record if the occurrence is removed.

Tasks with no checklist, form or procedure look exactly as they do now.


1. What FreeITSM has today

Mapped from the code (3.12.0 with #181), not from memory:

The work view needs Today Verdict
One task per scheduled day, earlier ones still open A repeat on a fixed schedule creates every occurrence on its date, whether or not the earlier ones are done βœ… Have it
The day an occurrence is for, kept apart from its due date and its completion scheduled_date (set once, never edited), due_date, completed_datetime and completed_by_id (#181) βœ… Have it
The period being checked (for example "Saturday's backups") Not recorded ❌ New (section 4)
Instructions The task description (copied to each occurrence by the repeat) and the checklist template's description ⚠️ Partly. A template step's description is not copied to the task, so steps on a task show only their titles
Checklist steps with mandatory markers, recorded values and Pass / Fail / Not applicable task_checklist_items (#181 added results, exception notes and step history) βœ… Have it
Fresh results on each new occurrence The repeat copies each checklist without its ticks, values or results βœ… Have it
A form linked to a task No link at all. A submission can point at a ticket (form_submissions.ticket_id), never at a task ❌ New (section 3)
A form filled in somewhere other than the Forms page The field rendering and conditional logic are shared (FormRender, FormLogic), but the filling and saving code lives inside forms/fill.php ⚠️ Needs extracting
Saved progress on a form form_drafts: one draft per person per form version, not tied to anything ⚠️ Saturday's and Sunday's drafts would overwrite each other
Server-side validation of required and conditional answers FormsService::submitForm() re-works out which fields are visible, drops hidden answers and checks required fields, formats and options βœ… Have it, reused unchanged
A retry that cannot submit twice Nothing stops a double submit ❌ New
Documents on a task The shared documents panel, scoped to the task's company (#181) βœ… Have it
Follow-up work A task can link one ticket and one change. A subtask is part of the task, not a follow-up from it ⚠️ Section 5
A completion check on every path ChecklistsService::assertTaskCompletionAllowed(), called before every open-to-closed change in updateTask, moveTask, the board's drag and subtask ticking, and so in the REST API too βœ… Have it. Extended, not copied (section 6)
History of what happened to an occurrence task_events and the History tab (#181) βœ… Have it. New event types
Evidence report, CSVs and the frozen copy of a removed occurrence TaskEvidence, TaskHistory::snapshot() (#181) βœ… Have it. Extended

2. Where #181 ends and #190 begins

Dr4inder's own line from the issue is the rule I am designing to:

"Reuse #181's occurrence history and evidence-reporting capabilities where applicable, rather than creating a separate compliance module or duplicating its data model."

Owned by #190 does
The occurrence (one task row per scheduled date) #181 and repeats Reuses it. The occurrence is the unit of work. There is no new "run" or "execution" record beside the task
scheduled_date, the due date in force at completion, who completed it #181 Shows them on the Work tab and adds only the period (section 4)
Checklist steps, values, results, exception notes, step history #181 / checklists Shows them on the Work tab through the same endpoints. No second checklist editor
History (task_events) #181 New event types for forms and follow-ups, written through TaskHistory::record()
The completion gate Checklists on tasks Widened into one task completion check covering checklists, required forms and follow-ups. It stays one function called from the same four places
Skip #181 Unchanged. Skipping is still not gated: not doing the work is the point
The evidence report and CSVs #181 Adds form answers, follow-ups and form exceptions to each occurrence's row
The frozen record of a removed occurrence #181 snapshot() also freezes the linked submissions and their answers
Form definitions, versions, validation, drafts Forms Reused. A submission still points at the exact form version it was made on, which is the "template/version context" acceptance criterion 8 asks for

3. Forms that belong to a task

Saying which forms a task needs

A new section on the task, Forms, beside Checklists. It sits behind the same kind of switch as checklists on tasks (Tasks β†’ Settings), so a team that does not want it never sees it. Adding a form to a task makes a row in a new table:

task_forms: task_id, form_id (the newest version when it was added), is_required, requirement_mode (warn or block, the same two levels checklists use), submission_id (empty until it is submitted), sort_order, who added it and when.

  • On a repeat, the forms travel to the next occurrence the way checklists do: the requirement is copied, the answers are not. Each occurrence starts unanswered (acceptance criterion 8). The copy points at the form's newest version at that moment, so a form edited between Saturday and Sunday is used in its new shape from Sunday, and Saturday's submission keeps pointing at the version it was made on.
  • One submission per form per occurrence. If a check genuinely needs two, it is a grid field or two forms on the task.
  • An optional form (not required) is offered on the Work tab but never stops completion.

Filling it in

The Work tab renders the form inside the task, with the same field types, sections, conditional questions, lookups and grids as the Forms page. To do that without a second form engine, the filling code in forms/fill.php moves into a shared FormFiller (render, read the answers, save a draft, submit) that both the Forms page and the task use. The server half is untouched: the task submits through FormsService::submitForm(), so required, conditional and format validation are exactly the same.

The submission is saved with a new column, form_submissions.task_id, and task_forms.submission_id is set in the same transaction, so a submission can never exist half-linked. A task-linked submission does not get its own route into the Forms module's workflows other than the ones the form already has: its on submit actions run as they do today, with the task's number added to what they receive.

Saving progress

A draft made inside a task belongs to that occurrence, and is shared by everyone who may work on it (decision 3). Saturday's and Sunday's half-finished forms stay separate, reopening Saturday's task brings back Saturday's draft, and a check one analyst started can be handed to another. The draft shows who saved it last, and when. A draft made on the Forms page has no task and behaves exactly as it does today (one per person per form).

So a task's draft is not a form_drafts row (whose key is one per person) but lives on the requirement itself: task_forms.draft_json, draft_by_id, draft_datetime and a revision number.

Two people with the same occurrence open

Asked for by @Dr4inder: a stale save must produce a clear conflict and keep the analyst's work, never silently overwrite somebody else's changes. So every save is conditional on the revision it started from:

  1. Opening the form reads the draft and its revision.
  2. Saving sends that revision. The server updates the row only if the revision is still the same (UPDATE … WHERE revision = ?), and bumps it.
  3. If somebody else saved in between, nothing is written. The answer is a conflict naming who saved and when, with their version. The Work tab keeps what this analyst typed on screen, says "Sam saved this form at 10:42 while you had it open", and offers Use theirs or Keep mine (which saves again on top of theirs, deliberately, with the new revision).

The same rule covers the submitted answers: submitting, and correcting an earlier submission, send the revision too, so two people cannot both submit the same occurrence's form, and a correction cannot replace a submission its author never saw.

Submitting twice

Every submit from the Work tab carries a client reference generated when the form is opened. form_submissions gets a unique key on task + client reference, so a retry after a timeout returns the submission that already exists instead of making a second one. It is the same shape I proposed for change evidence uploads in #194, so the REST API ends up with one idempotency pattern, not two.

Once it is submitted

  • A submission linked to a task can not be deleted from the Forms module while it is linked: it is that occurrence's evidence. If the occurrence is removed, the submission goes into its frozen record (section 7).
  • Correcting a submitted answer is a new submission that replaces it on the task, with the replaced one kept and shown in the History, and it is subject to the same revision check as a draft. That follows the #181 setting Changing a completed task's checklist: on an install that refuses changes after completion, the form is locked once the task is done, too.

Who can see and fill it

Embedding must not grant access (criterion 7), so:

  • Filling a form inside a task needs what filling it on the Forms page needs: access to Forms that is not Read only. Someone without it sees the form's name and "You need access to Forms to fill this in", and if the form is required, the Complete button says that is what is stopping it.

  • Reading a task's form answers is decided by one rule, written once (TaskForms::mayReadAnswers()) and used everywhere the answers appear - the Work tab, the History tab's form events, the evidence report on screen and in print, and the form-answers CSV:

    You may read an occurrence's form answers if you can see the task, and either you have access to Forms (as you would need to read the same submission there), or your role grants the new everyday permission Read form answers on tasks.

    Reading the task is never enough on its own (decision 4): task access does not quietly override Forms access. The new permission is how a Read only auditor (#181) is given exactly the answers on the occurrences they can already see, and nothing else in Forms - they still cannot open the Forms module, its other submissions or its designer. It is off unless a role grants it, so an upgrade gives nobody anything new.

    Where the rule says no, the place the answers would be shows that a form was submitted, by whom and when, and "You do not have access to these answers" - so the record is visibly complete without its contents. The CSV writes the same words in the answer column rather than leaving rows out, so its row count does not depend on who ran it.

  • Forms are install-wide today, and so are their submissions. A submission linked to a task takes the task's company, so in a multi-company install the Forms module's submissions list only shows it to people who can see that task. This is the same rule #181 applied to a task's documents.


3a. The procedure

Decision 7: the instructions belong in the Work view, from records FreeITSM already has.

  • Task instructions - the task's own description, which the repeat already copies to each occurrence.
  • Step descriptions - a checklist template step's description is now copied to the task's step, so each step is readable in place (until now a task's step showed only its title).
  • Procedure references - a task, or its repeat, can reference Knowledge articles (task_procedures: task, article, sort order). The Work tab shows each one in place, through Knowledge's own visibility rules: an article the viewer may not read is named, not shown. The repeat copies the references to each new occurrence, like checklists.
  • Supporting documents - the task's existing documents, as they are.

The text an occurrence was done against

@Dr4inder asked that later edits must not rewrite the instructions behind historical evidence. Knowledge articles have version numbers, but an ordinary save edits an article in place - only save as a new version keeps the old text. So the version number alone cannot promise the words.

When an occurrence is completed (or skipped), each referenced article's title and body as they stand at that moment are frozen into its record: task_procedure_snapshots holds each distinct text once (keyed by its hash, so 365 daily occurrences done against the same SOP store it once), and the occurrence's reference keeps the snapshot, the article's version number and the time. The evidence report shows "done against: Backup verification SOP, version 4", and the frozen text is one click away even after the article has been edited, archived or deleted. A removed occurrence keeps it in its #181 frozen record.

While an occurrence is open it shows the article as it is now, because that is what the analyst is working from.

Writing procedures in FreeITSM - the longer-term direction @Dr4inder describes, where instructions, forms, checklists, findings and evidence all live here - is what this builds towards: Knowledge is where the procedure is written, and the occurrence is where it is carried out.

4. One occurrence, three dates

The issue asks for the scheduled date, the period being checked and the actual completion to stay distinct. Two of the three already exist from #181. The third is new:

Column Set by Changes?
This occurrence is for scheduled_date (#181) The repeat, once Never
Period checked new period_start, period_end The repeat, from the rule, once Never
Due due_date The repeat, then people Yes, and #181 records every move
Done completed_datetime, completed_by_id (#181) Completing Cleared and recorded if reopened

The repeat gains one setting, This occurrence checks: nothing in particular (the default, so existing repeats are unchanged), the scheduled day, the day before, or the time since the previous occurrence. The period is worked out when the occurrence is created and never edited afterwards, like scheduled_date.

Saturday's check, done on Monday

Dr4inder's weekend case works today: the repeat creates Saturday's and Sunday's tasks on their days, and they can be completed on Monday as separate occurrences, each keeping its own date and records.

The one thing to be clear about is how #181's report judges them. If Saturday's task is due on Saturday and completed on Monday, the report calls it Late. That is accurate, but it may not be what a team means when its policy is "weekend checks are reviewed on Monday". So the repeat could optionally set when each occurrence is due, relative to its scheduled day (the same day, N days later or the next working day). Saturday's check is then for Saturday, checks Saturday's period, is due on Monday, and is On time when done on Monday, while a check done on Tuesday is still Late. Decision 2.


5. Follow-up work

A finding often needs a ticket or a task to fix it. Today a task links to at most one ticket (tasks.ticket_id), which is already taken by "the ticket this task belongs to", and a subtask is part of the task, not a follow-up from it.

So a new table, task_follow_ups: task_id, kind (ticket or task), the record's id, where it came from (a checklist step, or a form submission and field), who and when, and the client reference that stops a retry creating it twice.

From the Work tab:

  • Create a ticket or task. It opens prefilled with the occurrence's title, its scheduled date and the finding (the step's exception note, or the form answer), and is linked when saved.
  • Link an existing ticket or task, with the same pickers the task window uses for links.
  • Each follow-up shows its live status. Opening one follows the normal access rules, so a ticket the viewer cannot see is named by number only, as in the #181 report.

Whether an open follow-up stops the occurrence completing is a setting (decision 5). The default is that it does not: the check was performed and the finding recorded, which is exactly what completing means, and the follow-up stays open and linked.


6. Completing: one check, every path

The issue is right that a Complete button which checks things the board's drag does not would be worse than no check. #181 and the checklists work already solved where completion happens. There are four status writers, and each calls one gate before an open-to-closed change.

So I would widen that gate rather than add a second one. ChecklistsService::assertTaskCompletionAllowed() becomes a task completion check that gathers:

  • mandatory checklist steps outstanding (unchanged)
  • required forms with no submission (new)
  • open follow-ups, if the setting says they block (new)

Each item is warn or block, with the same company block all floor that already reaches tasks. It stays one function, called from the same four places, so the board, the right-click Mark complete, a subtask tick, the task window and the REST API all get it at once. The Work tab's progress bar and the Complete button ask the same function (?check=complete) before posting, so what the analyst is told is computed by the code that enforces it.

A completion that goes through with warn items outstanding is recorded, as checklists record today, and appears in the #181 report as an exception.

What "complete" means here

Completing says the review was performed and its findings recorded. It never turns a finding into a success:

  • A checklist step marked Fail stays a failure in the report (#181).
  • A form answer can be marked as an exception too. In the form designer, a choice field can mark some answers (for example Warning and Failure) as counts as an exception. The marking belongs to the form version, so it cannot change after the fact. Those answers then appear in the #181 report's exceptions, beside failed steps, with the explanation fields that the form's conditional logic made the analyst fill in.

Save failures

A failed save leaves the task open, keeps what was saved (submitted forms, ticked steps and drafts are separate saves, each already durable), and the Work tab lists what still needs attention. Complete is the last step and changes only the status, so a retry after a failure cannot create anything twice. Submissions and follow-ups are protected by their client references.


7. The evidence report and the frozen record (#181)

So the work done in the Work tab is still there when an auditor asks:

  • Report row. Each occurrence gains its forms (submitted by, when, which version) and its follow-ups (kind, number, status), and the period checked where the repeat records one. Form answers marked as exceptions count in the summary's exceptions.
  • Print. Each occurrence's form answers are printed under its checklist results, as the form showed them (labels, sections and the visible fields only).
  • CSV. A fourth file, form answers: one row per answer, with series id, task id, submission id, form version and field, so it joins to the other three. Follow-ups go in the existing evidence file.
  • History events. form_added, form_removed, form_submitted, form_replaced, follow_up_created and follow_up_linked, recorded through TaskHistory::record() and shown in the History tab.
  • A removed occurrence freezes its linked submissions, their answers and the form version's field labels into the same task_removed_records snapshot as its checklists. The submission rows are kept as well, with their task link, so the Forms module's record and the report agree. Like the rest of #181's record, none of the new tables has a foreign key that would delete evidence with its task.

8. Decisions

Answered by @Dr4inder on 19 Oct 2026.

# Question Decision
1 Where the Work view lives A Work tab in the large window, which a task with working material (a checklist, a form or a procedure) opens on by default. A personal preference turns that off; the panel and both layouts stay as they are
2 Weekend checks reviewed on Monday An optional when each occurrence is due on the repeat (for example the next working day). Same day stays the default. One occurrence per scheduled day, with scheduled date, period checked, due date and completion all distinct
3 A draft inside a task Shared by everyone who may work on the occurrence, showing who saved it last - and a stale save is a visible conflict that keeps the analyst's work (Saving progress)
4 Reading a task's form answers Not from reading the task alone. Forms access, or a new, narrow permission Read form answers on tasks, applied the same way in the Work tab, History, report and CSV (Who can see and fill it)
5 An open follow-up at completion Allowed by default, with the link and its live status kept. The review can be complete while the remediation is open
6 OK / Warning / Failure A form with conditional explanation and action fields. Checklist results stay Pass / Fail / Not applicable; the form's counts as an exception answers feed the #181 report
7 The procedure / SOP Task instructions, copied step descriptions, documents and references to Knowledge articles, with the text each occurrence was done against frozen into its record (section 3a)
The questions as they were put
# Question Options My suggestion
1 Where the Work view lives (a) A Work tab in the large window's tabs layout, which is also the tab the task opens on when it has work (a checklist, a form or a procedure) Β· (b) a third layout alongside Two columns and Tabs (a). It is what the issue sketches, it keeps the panel and both layouts as they are, and a task with no work shows no Work tab. Opening on the Work tab would be a personal preference, on by default
2 Weekend checks reviewed on Monday (a) Keep due = scheduled day, so Monday's review shows as Late in the #181 report Β· (b) Add when each occurrence is due to the repeat (section 4), so it can be On time (b), as an option. The default stays "the same day", so nothing changes on upgrade
3 A draft inside a task (a) Shared: anyone who works the occurrence sees and continues it Β· (b) Personal, as drafts are today (a). The occurrence is the unit of work, and a check started by one person and finished by another is normal. Who saved it last is shown
4 Reading a task's form answers (a) Anyone who can read the task, as with its checklist values Β· (b) Only people who also have access to Forms (a). The answers are that occurrence's evidence, and a Read only auditor on Tasks (#181) needs to see them. Filling still needs Forms
5 An open follow-up at completion Allowed (recorded) Β· Warn Β· Block. One setting in Tasks β†’ Settings β†’ Evidence Allowed, the default
6 Warning as a checklist result (a) Leave checklist results as Pass / Fail / Not applicable and use a form for OK / Warning / Failure Β· (b) Add Warning to checklist results too (a). A form already does conditional "explain this" fields, which a checklist step cannot. Adding Warning to steps is easy later if it is wanted
7 The procedure / SOP (a) The task description, plus each step's description, which is newly copied from the template Β· (b) Also a link to a knowledge article or document, carried by the repeat (a) now, (b) later. (a) needs no new concept. (b) is worth doing once (a) is in use

9. The order to build it in

Each phase is useful on its own and ships in its own release:

  1. Forms belong to a task. task_forms, form_submissions.task_id, shared drafts with the revision check, the client reference, the repeat copying the requirement, the widened completion check, the Read form answers on tasks permission and its one rule, history events, the report, the CSV and the frozen record. Filling still happens in a window that opens the shared FormFiller from the task, so the data model and the gate are proven first.
  2. The Work tab - the key milestone for a single entry point. Instructions, step descriptions, procedure references with their frozen text, checklists, forms inline, documents, progress and Complete, plus the keyboard, narrow-screen and phone work.
  3. Follow-ups. task_follow_ups, create and link from a step or an answer, the setting.
  4. Period and due offset on repeats, and counts as an exception in the form designer.
  5. REST API. GET /tasks/{id}/forms, and task_id with client_reference on POST /forms/{id}/submissions, so an integration can submit a check against an occurrence. Completion over the API already goes through the gate.

10. The issue's acceptance criteria

# Criterion Where
1 Reached through the normal task-opening flow; panel and layouts remain Decision 1
2 Instructions, steps and values, forms and evidence in the main area Sections 3 and 9 (phase 2)
3 Submissions and follow-ups linked to the right occurrence automatically Sections 3 and 5
4 Each daily occurrence keeps its own progress, forms and evidence; weekend ones completed later keep their day Sections 3 (drafts per occurrence) and 4
5 Required work checked on every write path; failures and retries cannot make false completions or duplicates Section 6, client references
6 The same records and save paths are reused; no parallel evidence store or editor Section 2
7 Permissions, conditional validation, closure rules, history and recurrence preserved; no extra access Section 3 (Who can see and fill it), section 6
8 Fresh results for each occurrence; history keeps the right task, period and version Sections 1, 3 and 4
9 Simple tasks stay simple; long content, keyboard, narrow screens and themes Decision 1, phase 2

11. Deliberately out of scope

As the issue says: no enforced order of steps, no new workflow or approval engine, and no separate compliance module. The Work tab is a layout over records that already exist. The report shows what was recorded, and a warning or failure stays a warning or failure.


Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally