Skip to content

Writing a Verb function

Tutorial 3 of 12 writes the Function behind Lease.renew and the test that gates every Change to it.

A Verb runs its default Function until a file gives it one. Leaseline declares a Lease Noun, and nouns/Lease.renew.ts says what renewing does.

What you build

You write the Function behind Lease.renew: it extends the end date by the term and raises the rent within the increase the manager allows. Then you write the test that every Change to the Function must pass before it merges.

Steps

Terminal4 steps
  1. To give the Verb its Function, create the file beside the Noun. Its name binds it; nothing registers it:

    nouns/Lease.renew.ts
    export default async (lease: Lease, input: LeaseRenewInput, $: $) => {
      const end = new Date(lease.endsOn)
      end.setMonth(end.getMonth() + input.months)
      const rent = lease.rent && { ...lease.rent, amount: Math.round(lease.rent.amount * (1 + (input.increase ?? 0))) }
      return $.Lease.update({ id: lease.$id, endsOn: end.toISOString().slice(0, 10), rent })
    }

    The Function never sets status. When the Action succeeds, the Event Lease.renewed moves the lease to renewed.

  2. To pin the rule down, write its test beside it:

    nouns/Lease.renew.test.ts
    import { expect, run, test } from 'startup-builder/test'
    
    const lease = { $id: 'lease_4loEed1JG4', endsOn: '2026-12-31', rent: { amount: 1800, currency: 'USD' } }
    
    test('a 3% increase raises the rent', async () => {
      const { result, calls } = await run({ lease: 'lease_4loEed1JG4', months: 12, increase: 0.03 }, { Lease: { get: () => lease } })
      expect(result.rent.amount).toBe(1854)
      expect(calls).toEqual(['Lease.update'])
    })
  3. To run the case, save either file. npx startup-builder dev reruns every case on each save.

  4. To see which Function a call ran, renew one lease in test and read its Action:

    npx startup-builder do Lease.renew --json '{"lease":"lease_4loEed1JG4","months":12,"increase":0.03}'

    The Action names the Function Version it ran, with its input, output and cost.

Result

Lease.renew runs your code, and a Change that breaks the 3% case cannot merge. Function kinds shows the same Verb as code, a model call, an agent and a person. Checkpoint leaseline@ch03 (status planned) adds nouns/Lease.renew.ts and its test.

Next steps