Skip to content

Engineer Pre-Departure Checklist

A train engineer must complete three independent inspection streams — mechanical, electrical, and safety systems — before signing off for departure. All three run in parallel; the sign-off is only available once all three are done.

Features shown: ForkState, JoinState mode:'all', inline guard with Zod literal, parallel execution trace.

Workflow diagram

reported-for-duty
     │ BRIEFING_RECEIVED

briefed ──START_INSPECTION──▶ inspection-fork ⑂
                              /       |        \
                        mechanical electrical safety-systems
                              \       |        /
                          inspections-joined ⑁ (all)
                                    │ SIGN_OFF
                               signed-off
                                    │ DEPART
                               departed ✓

Full code

ts
import { z } from 'zod';
import { createWorkflow } from 'flowyd';
import { MermaidExporter } from 'flowyd/visualization';

// ── Action schemas ──────────────────────────────────────────────────────────

const BriefingSchema = z.object({
  trainId: z.string(),
  routeCode: z.string(),
  shiftTime: z.string(),
});

const InspectionSchema = z.object({
  technicianId: z.string(),
  notes: z.string().optional(),
});

const SignOffSchema = z.object({
  engineerId: z.string(),
  certifies: z.literal(true), // Zod literal — must be exactly `true`, not just truthy
});

const DepartSchema = z.object({
  platform: z.number().int().min(1),
  scheduledAt: z.string(), // ISO 8601
});

// ── Workflow definition ─────────────────────────────────────────────────────
//
// `addFork` targets and `addJoin` requires both autocomplete to the accumulated
// TStates union. Branch states must be registered before the fork that targets them.

const engineerChecklist = createWorkflow({ name: 'engineer-predeparture-checklist' })
  .defineAction('BRIEFING_RECEIVED', BriefingSchema)
  .defineAction('START_INSPECTION', z.object({}))
  .defineAction('MECH_OK', InspectionSchema)
  .defineAction('ELEC_OK', InspectionSchema)
  .defineAction('SAFETY_OK', InspectionSchema)
  .defineAction('SIGN_OFF', SignOffSchema)
  .defineAction('DEPART', DepartSchema)

  .addStep('reported-for-duty', { label: 'Reported for Duty' })
  .addStep('briefed', { label: 'Briefed' })
  .addStep('mechanical', { label: 'Mechanical Check' })
  .addStep('electrical', { label: 'Electrical Check' })
  .addStep('safety-systems', { label: 'Safety Systems Check' })
  .addFork('inspection-fork', {
    label: 'Inspection Fork',
    targets: ['mechanical', 'electrical', 'safety-systems'],
  })
  .addJoin('inspections-joined', {
    label: 'Inspections Complete',
    requires: ['mechanical', 'electrical', 'safety-systems'],
    mode: 'all',
  })
  .addStep('signed-off', { label: 'Signed Off' })
  .addStep('departed', { label: 'Departed' })

  .setInitial('reported-for-duty')
  .setTerminal(['departed'])

  .addTransition({ from: 'reported-for-duty', to: 'briefed', on: 'BRIEFING_RECEIVED' })
  .addTransition({ from: 'briefed', to: 'inspection-fork', on: 'START_INSPECTION' })
  .addTransition({ from: 'mechanical', to: 'inspections-joined', on: 'MECH_OK' })
  .addTransition({ from: 'electrical', to: 'inspections-joined', on: 'ELEC_OK' })
  .addTransition({ from: 'safety-systems', to: 'inspections-joined', on: 'SAFETY_OK' })
  .addTransition({
    from: 'inspections-joined',
    to: 'signed-off',
    on: 'SIGN_OFF',
    guard: (ctx) => ctx.payload.certifies === true,
  })
  .addTransition({ from: 'signed-off', to: 'departed', on: 'DEPART' })

  .build();

// ── Execution ───────────────────────────────────────────────────────────────

async function runChecklist() {
  const instance = engineerChecklist.createInstance('ENG-042-20240520-0600');

  // Step 1: Morning briefing
  await instance.dispatch('BRIEFING_RECEIVED', {
    trainId: 'ENG-042',
    routeCode: 'NS1',
    shiftTime: '06:00',
  });
  console.log(instance.getCurrentStates()); // ['briefed']

  // Step 2: Kick off parallel inspections
  // ForkState is transient — it completes in the same engine tick and activates all targets
  await instance.dispatch('START_INSPECTION', {});
  console.log(instance.getCurrentStates()); // ['mechanical', 'electrical', 'safety-systems']

  // Step 3: Each technician clears their stream — order does not matter
  await instance.dispatch('ELEC_OK', { technicianId: 'ELEC-7', notes: 'All circuits nominal' });
  console.log(instance.getCurrentStates()); // ['mechanical', 'safety-systems']

  await instance.dispatch('SAFETY_OK', { technicianId: 'SAFE-3' });
  console.log(instance.getCurrentStates()); // ['mechanical']

  // Final inspection — JoinState auto-activates once all three complete
  await instance.dispatch('MECH_OK', { technicianId: 'MECH-12', notes: 'All within spec' });
  console.log(instance.getCurrentStates()); // ['inspections-joined']

  // Step 4: Engineer signs off
  await instance.dispatch('SIGN_OFF', { engineerId: 'ENG-042', certifies: true });
  console.log(instance.getCurrentStates()); // ['signed-off']

  // Step 5: Depart
  const result = await instance.dispatch('DEPART', {
    platform: 3,
    scheduledAt: '2024-05-20T06:00:00+08:00',
  });
  console.log(result.success); // true
  console.log(instance.isTerminal()); // true
  console.log(instance.getSnapshot().history.length); // 7

  // ── Guard demo: certifies must be the literal `true` ──────────────────────
  // The Zod schema uses z.literal(true) — passing `false` is caught twice:
  //   1. TypeScript rejects it at compile time (type 'false' is not assignable to 'true')
  //   2. Zod throws ZodError at runtime if somehow the wrong value arrives

  // ── Visualize ────────────────────────────────────────────────────────────
  console.log(MermaidExporter.export(engineerChecklist.getDefinition(), instance.getSnapshot()));
}

runChecklist().catch(console.error);

What to notice

ForkState is never in getCurrentStates. After dispatching START_INSPECTION, the fork completes immediately and the three inspection states are what's active. The fork is a routing node, not a resting place.

JoinState activates automatically. After the third inspection clears, the engine's fixed-point loop detects that all requires states are completed and activates inspections-joined in the same dispatch call. No extra action needed.

Zod literal(true) for boolean safety. certifies: z.literal(true) means the type is true, not boolean. Passing false is rejected at compile time. This pattern is useful for "I explicitly confirm this" checkboxes.

MermaidExporter.export(definition, snapshot) overlays live status. The snapshot colours states by their current status — completed in green, active in blue.

Released under the MIT License.