byrcsc/laravel-checklist

Run versioned Laravel checklists from template authoring through completion and review, with evidence, scoring, reports, and a tamper-evident audit trail.

Maintainers

Package info

github.com/byrcsc/laravel-checklist

Forum

Documentation

pkg:composer/byrcsc/laravel-checklist

Transparency log

Fund package maintenance!

Buy Me A Coffee

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-09 09:56 UTC

This package is auto-updated.

Last update: 2026-08-09 13:37:13 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub PHPStan Action Status Total Downloads

Vehicle checks, site inspections, maintenance jobs, opening and closing routines: work that records answers to a fixed list of questions, and that somebody may have to account for months later.

Laravel Checklist runs that work from template authoring through completion and review, with evidence, scoring, reports, and a tamper-evident audit trail. Publishing a template freezes its questions, so a checklist answered last year still shows the questions it was actually asked.

The package supplies the engine and the Eloquent models. It has no user interface and registers no routes.

Read the documentation for the full reference. This README covers installation and the main flows.

Laravel Tested PHP versions
12.x 8.3, 8.4
13.x 8.3, 8.4

Tested on MySQL and PostgreSQL.

Installation

composer require byrcsc/laravel-checklist
php artisan vendor:publish --tag="checklist-migrations"
php artisan migrate

Publish the configuration before migrating if you need custom table names or non-integer user identities:

php artisan vendor:publish --tag="checklist-config"

Notification wording publishes separately, and only if you want to change it:

php artisan vendor:publish --tag="checklist-translations"

Every configuration key is documented in Configuration.

Key terms

A template publishes immutable versions holding sections and items. A checklist is one run of a version, optionally about a subject such as a vehicle. A response holds the answer to one item, and every change is recorded in an append-only audit trail.

Key terms covers the rest.

Item types

The set below is fixed in v1. Validation settings live on the item, and each type decides how its answer is validated, stored, and scored.

Type Config Stored as Score
checkbox none boolean checked 1, unchecked 0
yes_no none boolean yes 1, no 0
pass_fail none boolean pass 1, fail 0
text max_length string unscored
number min, max float unscored
rating min, max (both required, min < max) int normalized from 0 to 1
date none Y-m-d string unscored
select options, multiple option value, or a list of them the option's score, or their mean

Single and multiple select share one item type, separated by the multiple flag rather than by a type of their own.

An answer fails when its score is exactly 0. The same definition decides whether the item-failed notification is sent and whether a critical item fails the checklist, so the two always agree. Unscored answers never fail, and an unanswered item is not a failed one.

Word yes/no questions so that the passing answer scores. "Any body damage?" scores an undamaged vehicle at 0 and reports it as a failure, because the accurate answer is no. "Is the bodywork free of damage?" scores an undamaged vehicle at full weight. Nothing in the package enforces this. The scoring is correct either way; only the second question maps a good vehicle to a passing score.

Full details in Items and sections.

Quick start

Author and publish a template with the builder. It validates the whole template before writing anything: rule targets must exist, branches must not be orphaned, and the section tree must stay inside the depth limit.

use ByRcsc\LaravelChecklist\Authoring\TemplateBuilder;

$version = TemplateBuilder::make('Vehicle pre-trip inspection')
    ->passThreshold(80)
    ->section('Exterior', function (TemplateBuilder $builder): void {
        $builder->passFail('Tires are roadworthy')->critical()->weight(2);
        $builder->passFail('No fluid leaks underneath')
            ->requireEvidenceWhen('fail', 'photo');
        $builder->yesNo('Bodywork is free of damage?');
    })
    ->section('Cabin', function (TemplateBuilder $builder): void {
        $builder->checkbox('Registration and insurance present');
        $builder->rating('Interior cleanliness', min: 1, max: 5);
    })
    ->publish();

Nothing is written until publish(), and it writes everything in one transaction. A template that fails validation leaves no rows behind.

To edit a published template, draft the next version:

$draft = $version->newDraft();   // deep copy as version N+1, lineage carried
$draft->items()->where(...)->first()->update(['label' => 'Reworded']);
$v2 = $draft->publish();         // version N is left exactly as it was

Give any model checklists:

use ByRcsc\LaravelChecklist\Concerns\HasChecklists;

class Vehicle extends Model
{
    use HasChecklists;
}

Run one:

$checklist = $version->start(subject: $vehicle, assignedTo: $driver);

$checklist->answer($item, 'fail');   // validated and cast by the item's type,
                                     // and auto-transitions to in_progress

$checklist->addEvidence($response, $uploadedFile, 'photo');  // via Storage

$checklist->submit();   // refuses while anything required is unanswered,
                        // reporting everything outstanding at once

$checklist->review($inspector, outcome: 'accepted');

A checklist moves through pending, in_progress, completed, and reviewed. The first answer starts it. You can reopen it from completed or reviewed, which clears the review and is recorded in the audit trail. Any other transition throws.

Work through a full run in the Quick start.

What the package does

Rules. Two kinds, both written against the item whose answer decides them. Requirement rules (requireEvidenceWhen(), requireNoteWhen()) ask for more than an answer. They are reported when the answer is given and enforced at submit(). Visibility rules (showWhen(), hideWhen()) change which questions apply, and hiding a section hides everything inside it. If a show rule and a hide rule both apply, the item is hidden. Hidden answers and their evidence are kept and marked not applicable, so they are left out of scoring, progress, and completion checks. Reopen the branch and the earlier answer is still there.

Scoring. Each scorable answer maps to a value from 0 to 1, and each item can carry a weight. The checklist score is the earned weight divided by the weight of the items that applied, so a hidden question lowers the total the score is measured against instead of counting as a failure. Section scores and the total are calculated once at submission and stored, so a report run years later returns what was recorded then. A checklist with nothing scorable has no score at all, which is not the same as a score of 0. A failed critical item fails the checklist whatever the score.

Evidence. Photos, files, and signatures attach to an answer and are written through Laravel Storage, with no media-library dependency. Each row records the disk, path, MIME type read from the file contents, size, SHA-256, and who captured it when. Files are stored under a new ULID, and extensions that a web server might execute are dropped. The package writes and deletes files. Your application decides who may see one and builds the URL.

Recurrence. A schedule pairs a template with a frequency, a timezone, and optionally a subject and an assignee. Daily, weekly, monthly, and every-N-days are supported; iCal RRULE expressions are not. One command creates the checklists that are due:

Schedule::command('checklist:generate-due')->everyFifteenMinutes();

Each due date is calculated in the schedule's own timezone from its own next_due_at, so running the command late does not shift the schedule. However long the command has been down, a schedule creates at most one catch-up checklist.

Events and notifications. Every transition fires an event, and each one implements ShouldDispatchAfterCommit, so it is safe to wrap a checklist operation in your own transaction. The package also ships mail and database notifications for every lifecycle event, which most engine packages leave to the application. They are included because an unassigned or overdue checklist usually needs someone told before it gets done. One RecipientResolver class decides who is notified, and turning the notifications off leaves the events running so you can send your own.

Audit trail. Every change is recorded in an append-only checklist_actions table, both per item and per checklist. Rows cannot be updated or deleted, and one class writes all of them. Each row's SHA-256 covers its own contents and the hash of the row before it, and the checklist stores the latest hash.

php artisan checklist:verify   # exits non-zero if any chain is broken

This detects an edited row, a deleted row, and a row inserted in the middle. It does not stop someone who can already write to the table: the hash uses no secret, so that person can recalculate the whole chain and verification will pass. Treat it as a strong check against mistakes and casual tampering, not as a cryptographic guarantee.

Export and reports. The package generates no PDFs; it assembles the data and documents how to render it. $checklist->toExport() returns the whole checklist in reading order. Scores come from what was stored at submission and are never recalculated, and answers a branch set aside are included and flagged rather than dropped. Eleven query scopes and five reports read the stored results. mostFailedItems() groups by lineage ULID, so rewording a question does not restart its count.

Composing with sibling packages. This package depends on none of its siblings. byrcsc/laravel-comments, byrcsc/laravel-approval, and byrcsc/laravel-assignment attach through polymorphic relations for discussion, multi-stage sign-off, and assignment routing.

Not included in v1

No user interface. No authorization, file URLs, or thumbnails. No PDF rendering, though the documentation includes a Blade and dompdf recipe. No threaded comments, assignment routing, or multi-stage approval; the sibling packages above cover those. No iCal RRULE recurrence. No repeatable sections, such as one copy per room. No custom item types, though the internal type handlers mean a public registry could open in a later minor release. No cached or materialized statistics tables.

Testing

composer test        # the suite
composer analyse     # PHPStan, level max
composer format      # Pint

The workbench/ directory holds a demo application that installs the package the way a real application does and drives one checklist through every feature:

composer build                                   # migrate and seed from clean
php vendor/bin/testbench checklist:demo          # the whole engine, narrated

It exits non-zero if no notification was sent or an audit chain fails to verify, and CI runs it on every push. workbench/README.md lists what it seeds and which JSON routes it serves. It is the quickest way to see how an integration fits together before writing your own.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for how to set up, the three checks every pull request must pass, where tests go, and what falls outside the package's scope. Everybody taking part is expected to follow the Code of Conduct.

Security

Please review our security policy on how to report security vulnerabilities. Do not open a public issue for one.

Credits

License

The MIT License (MIT). Please see License File for more information.