01Facilities · Business workflow

Build a maintenance app from report to resolution.

Give employees a clear way to report issues and technicians a record of the work. Connect recurring reports to earlier repairs so the next investigation starts with useful context.

CanLang builds the app
Records, permissions, workflows and connected screens.
Soda OS runs the factory
AI development, persistent Projects and reviewed changes.

Illustrative application and company rules. CanLang also works independently of Soda OS.

View CanLangBack to app
Maintenance.canExcerpt

Work notes and resolution

When
 crud Location by=technician fields=name,building,floor delete=none
 crud Ticket by=members fields=location,issue,priority update=none delete=none
 # Append a visit or diagnostic note while a ticket is open.
 scenario record_work(ticket:Ticket,note:text) by=technician
  require ticket.status==open and trim(note)!=""
  do create WorkNote {parent=ticket,note=trim(note)} as work_note
 # Resolve the issue with a description of the repair.
 scenario resolve(ticket:Ticket,resolution:text) by=technician
  require ticket.status==open and trim(resolution)!=""
  do set ticket {status=resolved,resolution=trim(resolution),resolved_by=actor,resolved_at=now}

Maintenance · App preview

Maintenance
MT-042 · Sam · 6 MarOpen

Air conditioning is not cooling

Jordan · Work note

Supply air is warm. Checking cooling controls.

Illustrative application interface · sample records.

02CanLang · Application model

People, records and rules.

Your department decides how the work should run. This example expresses those decisions in CanLang: the records it stores, who can access them, and the rules each action must follow.

Records the CanLang app keeps

Illustrative first-version records and relationships
LocationFloor 2Contains this reported issue
Ticket · OpenAir conditioning is not cooling
Reporter
Sam
Priority
Routine
Work note · Jordan, technician
Airflow is present; supply air is warm. Inspecting the cooling controls.
6 March · 10:18

Text, author and time stay connected to this ticket.

A ticket connects a named location, issue, routine/urgent priority and reporter. Work notes retain text, author and time; the resolution records its details, resolver and time.

First-version workflow

  1. Open
  2. Resolved

People and app access

Employee
Report a location, issue and priority; read their own reports and associated work notes.
Technician
Maintain locations, read the queue, add work notes to open tickets and resolve issues.

What these permissions mean

Expected behavior · illustrative cases, not executed test results

Allowed
A technician resolves an open ticket with a nonempty resolution.
Refused
An employee without technician authority resolves the ticket.
CanLang · Explicit business rules

What the app protects

The department sets these requirements. The CanLang application expresses the permissions and workflow restrictions.

  1. Only technicians add work notes to open tickets and resolve issues.

  2. A resolution must be nonempty. Employees cannot resolve tickets.

  3. Repeated resolution and changes to completed ticket details are refused.

  4. Ordinary employees follow their own reports and notes; technicians read the queue.

These are application permissions and business rules. Soda OS and Forgejo govern the separate development and code-review process in section 04.

See how these records and responsibilities connect
Illustrative workflow · Facilities
Floor 3
Floor 2Air conditioning
Floor 1
  1. Report
  2. Work notes
  3. Resolution
Sam reports. A technician investigates.

Each work note retains its text, author and time. A nonempty resolution records who resolved the issue and when.

Resolved ticket details stay protected. A second resolution is refused.

Read this app’s CanLang source
03CanLang · Application experience

Use the app. Check the result.

Follow the first-version workflow through its screens, then check that the app allows the right actions and protects the right records.

A walkthrough of the first version

  1. Sam reports the issue

    Sam can follow this report. Other ordinary reporters cannot read it.

  2. A technician records work

    Each note belongs to the open ticket and keeps its author, text and time.

  3. Resolution is recorded

    The resolved ticket and its work history are protected. A second resolution is refused.

View CanLangBack to app
Maintenance.canExcerpt

Work notes and resolution

When
 crud Location by=technician fields=name,building,floor delete=none
 crud Ticket by=members fields=location,issue,priority update=none delete=none
 # Append a visit or diagnostic note while a ticket is open.
 scenario record_work(ticket:Ticket,note:text) by=technician
  require ticket.status==open and trim(note)!=""
  do create WorkNote {parent=ticket,note=trim(note)} as work_note
 # Resolve the issue with a description of the repair.
 scenario resolve(ticket:Ticket,resolution:text) by=technician
  require ticket.status==open and trim(resolution)!=""
  do set ticket {status=resolved,resolution=trim(resolution),resolved_by=actor,resolved_at=now}

Illustrative first-version application

Maintenance
WorkspaceWork queue Internal app
Maintenance / Technician queue

Maintenance tickets

1 open · 1 resolved · Floor 2

MT-042 / Floor 2Open

Air conditioning is not cooling

Reported by Sam · 6 March, 08:40

Work notes

  1. Jordan · Technician

    Airflow is present; supply air is warm. Inspecting the cooling controls.

View report activity
  1. Issue reported

    Sam · 6 March, 08:40

    The Floor 2 air conditioning is running but the room is not getting cooler.

  2. Investigation note added

    Jordan · Technician · 6 March, 10:18

View resolved ticket · MT-031Resolved
MT-031 / Floor 2

Air conditioning is not cooling

Reported by
Casey · 6 March, 08:40
Work note
Inspected the control panel and found a loose connection.
Note author
Jordan · Technician · 6 March, 10:18
Resolution
Secured the connection. Cooling restored and checked at the vents.
Resolved by
Jordan · 6 March, 14:32

Earlier report, work notes and resolution are protected.

Illustrative application interface. The linked CanLang browser demo has the smaller scope described below.

Your department · Acceptance checklist

Does this version do the job?

Use these expected outcomes to accept the CanLang app. Try the relevant roles, including actions that must be refused.

Checks to perform on the illustrative first version; these are not reported test results.

Maintenance tickets · First version

Expected outcomes

  1. Employees report and follow their own issues; unrelated reporters cannot read them.

  2. A technician reads the queue and appends a note while a ticket is open.

  3. Only a technician resolves with a nonempty resolution; actor and time are recorded.

  4. Repeated resolution and changes to completed facts are refused.

Reference: the sample records behind these checks

Sam reports the issue

Location
Floor 2
Issue
Air conditioning is not cooling
Status
Open

A technician records work

Work
Investigation note recorded
Authorship
Technician and time retained
Status
Open

Resolution is recorded

Resolution
Nonempty resolution retained
Authorship
Resolver and time recorded
Status
Resolved

Try a focused CanLang demo

The compact demo covers reporting and technician resolution. Larger source adds locations, priority, work notes and resolution details. Recurrence links and link-change history belong to a separate continuation version.

Try the maintenance example in CanLang
04Soda OS · Software factory

Develop and review the app.

Your department defines the result. Soda OS coordinates authorised coding agents in a persistent Project; Forgejo records the work and applies the review and merge policy.

Start from the business request

“We need employees to report maintenance issues and follow their own reports. Technicians need a queue, work notes and recorded resolutions, with completed details protected.”

Follow the development process

Move through the stages below: accepted direction, dependent work items, CanLang source, independent review and policy-governed integration.

Corrections and human approval apply where required by repository policy. Merging code and deploying the application are separate actions.

The request, work items and review cases are illustrative, not a recording of a completed factory run.

1 / 6 · Facilities owns the direction

Agree on a clear path from report to resolution.

Facilities opens New Project and answers “What do you want to build?” Explore ideas develops the request: Sam reports that Floor 2’s air conditioning is not cooling; a technician resolves the ticket. Facilities accepts or changes the experience in “Review your product direction”.

What carries forward

An accepted maintenance workflow. Material questions return to Facilities instead of becoming hidden assumptions.

Example accepted direction

Example accepted direction

Maintenance reports, with a technician queue

  1. Capture a useful report

    Keep location, issue, reporter and status together. New reports start open and record the employee who submitted them.

  2. Give each role the right view

    Employees read their own reports. Technicians read the full maintenance queue.

  3. Let technicians resolve open tickets

    Require the technician role and open status. Block repeated resolution, lock resolved details and keep ticket updates and deletion unavailable.

Illustrative workflow artifact from the selected department journey.

2 / 6 · Soda coordinates the work

Plan the report, queue and resolution in order.

Authorize a finite planning allowance and choose a repository. Soda creates native Forgejo work items with acceptance criteria and explicit dependencies. Execution needs the repository’s authorization, accounts and capacity.

What carries forward

Accepted issue criteria and prerequisite outcomes. Unresolved dependencies or unanswered requirements interrupt the work.

Example native work items

Example native work items

A dependency plan for the Floor 2 report

  1. Define the Ticket record and access

    Store location, issue, reporting actor and open status. Employees read their own reports; technicians read the full queue.

  2. Implement reporting and resolution

    Depends on the record and access rules. Accept reports; require technician authority and open status before resolving a ticket.

  3. Connect the reporting form and queue

    Depends on records and operations. Verify Sam’s own-ticket view, the technician’s full queue and refusal of employee or repeated resolution.

Illustrative workflow artifact from the selected department journey.

3 / 6 · The coding agent implements

Make resolution follow the ticket’s rules.

Soda assesses readiness and prepares the repository’s persistent Project and assigned coding checkout. The selected coding agent receives the accepted issue, repository context and authorized account access, then reports the candidate and evidence. Soda OS publishes the pull request.

What carries forward

A candidate pull request at an exact revision. Spaces shows live agent activity and intervention needs alongside Forgejo.

Maintenance.can · source excerpt

Own reports, shared queue and guarded resolution

Given
 role technician
 Ticket { location:text trim, issue:text trim, reported_by:user server=actor, status:enum(open,resolved)=open }
 policy Ticket read=members where=row.reported_by==actor
 policy Ticket read=technician
 lock Ticket fields=location,issue,reported_by when=row.status==resolved
When
 crud Ticket by=members fields=location,issue update=none delete=none
 # Resolve an open maintenance ticket.
 scenario resolve(ticket:Ticket) by=technician
  require ticket.status==open
  do set ticket {status=resolved}
4 / 6 · Another agent checks the candidate

Check who can resolve, and when.

A separate agent session reviews the exact candidate alongside required checks. Findings return to the coding role; revised candidates receive fresh checks, with further independent review when evidence or repository policy requires it.

What carries forward

Findings and evidence belong to the revised candidate. These cases illustrate review expectations rather than report a completed run.

Illustrative review and correction

Illustrative review and correction

Reporting an issue does not authorize resolution

  1. Finding: an employee could resolve a report

    If a candidate allowed any member to resolve a ticket, Sam could close the Floor 2 report without technician authority. That violates Facilities’ accepted requirement.

  2. Correction: guard the role and state

    The coding role restricts resolution to technicians and requires an open ticket, as in the source above.

  3. Fresh review: exercise the accepted cases

    A technician can resolve Sam’s open report; Sam as employee cannot. Refuse a second resolution and changes to resolved details. Verify employees see their own reports while technicians see the full queue.

Illustrative workflow artifact from the selected department journey.

5 / 6 · Forgejo policy governs integration

Integrate when the current policy permits it.

Soda merges automatically when required review and checks pass for the exact candidate and repository policy permits it. When policy requires human approval, that approval is part of the path. Changed or missing evidence prevents integration.

What carries forward

Confirmed integration lets Soda reassess dependent work. Merge and application deployment remain separate actions.

Integration conditions

Integration conditions

The candidate must satisfy all three

  1. The revision still matches

    The pull request’s current candidate is the revision that was checked and reviewed.

  2. Required evidence is current

    Required checks, review and any required human approval satisfy the repository’s rules.

  3. Native merge is confirmed

    Forgejo records integration and the corresponding issue outcome. A published pull request alone does not establish integration.

Illustrative workflow artifact from the selected department journey.

6 / 6 · Facilities and employees use the workflow

Turn Sam’s report into a resolved ticket.

The same CanLang source describes the ticket, access rules, resolution action and screen. Try the matching browser example: report Floor 2’s air conditioning issue as Sam, then switch to the technician to resolve it.

What carries forward

A tool shaped around Facilities’ work. The next accepted improvement enters the same factory process.

Try the maintenance example
View CanLangBack to app
Maintenance.canExcerpt

Work notes and resolution

When
 crud Location by=technician fields=name,building,floor delete=none
 crud Ticket by=members fields=location,issue,priority update=none delete=none
 # Append a visit or diagnostic note while a ticket is open.
 scenario record_work(ticket:Ticket,note:text) by=technician
  require ticket.status==open and trim(note)!=""
  do create WorkNote {parent=ticket,note=trim(note)} as work_note
 # Resolve the issue with a description of the repair.
 scenario resolve(ticket:Ticket,resolution:text) by=technician
  require ticket.status==open and trim(resolution)!=""
  do set ticket {status=resolved,resolution=trim(resolution),resolved_by=actor,resolved_at=now}

Maintenance · App preview

Maintenance
MT-042 · Sam · 6 MarOpen

Air conditioning is not cooling

Jordan · Work note

Supply air is warm. Checking cooling controls.

Illustrative application interface · sample records.

Read the application outcomes

Floor 2 → Air conditioning not cooling → Resolved

  • Sam follows Sam’s reports

    Record a location and issue, then see the report’s status in the personal list.

  • The technician follows the full queue

    See every reported issue and resolve open tickets through the same workflow.

  • Resolution preserves the report

    Employees cannot resolve tickets; repeat resolution is refused. The browser example mirrors the rules with local page state and a reset control.

Read the factory handbook
Where this walkthrough comes from

The factory sequence and interface names follow the product documents and source code. Application behavior follows the matching complete examples on the CanLang page.

05CanLang app · Continued in the Soda OS factory

Connect a new report to an earlier repair.

The business changes its requirements. Revise the CanLang app in the same persistent Project, use the factory to develop and review the change, and check what must keep working.

What changes in the application

Illustrative related reports · Floor 2
New ticketOpen · Floor 2

Air-conditioning report

  • Own reporter and work notes
  • Own eventual resolution

Technician links the same-location repair with a reason

Earlier repair historyResolved · Floor 2

Air-conditioning repair

  • Original notes retained
  • Original resolution retained

The link adds context, not proof of the same cause. Reading the history requires access to both tickets.

Initial rule
Each occurrence has its own report, work notes and resolution.
New rule
A technician may connect a new open ticket to one earlier resolved ticket at the same location, with a reason. The link supplies investigation context; it does not prove an identical cause.
What stays protected
Both histories remain independent. Linking grants no access: a cross-ticket reference and preview require permission to read both records. Resolved relationships stay protected.

The revised app experience

View CanLangBack to app
MaintenanceRecurrence.canRule-change excerpt

Connect a new occurrence to an earlier repair

When
 crud Location by=technician fields=name,building,floor delete=none
 crud Ticket by=members fields=location,issue,priority update=none delete=none
 # Append a visit or diagnostic note while a ticket is open.
 scenario record_work(ticket:Ticket,note:text) by=technician
  require ticket.status==open and trim(note)!=""
  do create WorkNote {parent=ticket,note=trim(note)} as work_note
 # Set, correct or remove the one earlier-ticket reference while open.
 scenario relate(ticket:Ticket,earlier:Ticket?,reason:text) by=technician
  require ticket.status==open and trim(reason)!=""
  require earlier==null or (earlier!=ticket and earlier.status==resolved and earlier.location==ticket.location)
  do
   create LinkChange {ticket,previous=ticket.earlier,selected=earlier,reason=trim(reason)} as change
   set ticket {earlier}
 # Resolve the issue with a description of the repair.
 scenario resolve(ticket:Ticket,resolution:text) by=technician
  require ticket.status==open and trim(resolution)!=""
  do set ticket {status=resolved,resolution=trim(resolution),resolved_by=actor,resolved_at=now}
Illustrative CanLang rule-change excerpt · open the complete revised source for records, permissions and interfaces.Read the complete source

Maintenance · After a rule change

Maintenance
WorkspaceWork queue Internal app
Maintenance / Technician queue

New occurrence

MT-048 · Reported by Sam · 21 March

MT-048 / Floor 2Open

Air conditioning is not cooling

Reported by Sam · 21 March, 08:40

Work notes

  1. Jordan · Technician

    Airflow is present, but the supply air is warm. Investigating this occurrence.

Illustrative application interface · sample records.

Read the full rule, protections and review checks
Illustrative recurring report · technician view

Same location. Two separate histories.

Floor 2Air conditioning is not cooling
Same-location relationship required
Earlier ticketResolved
  1. Original reportOriginal reporter and description
  2. Original work notesText, author and time retained
  3. Recorded resolutionOriginal resolver and time retained

This completed history never changes.

Technician selectsOne earlier resolved ticketReason recordedContext, not proof of the same cause
New occurrenceOpen
  1. New independent reportIts own reporter and description
  2. New investigationIts own work notes
  3. Eventual resolutionBelongs to this new ticket

While open: correct or remove a link with who, when and why. On resolution: protect the relationship.

A relationship does not grant access.

Show a cross-ticket reference and preview only to someone who can read both tickets. Never reveal another reporter's title, notes or resolution through the link.

Technicians use their existing queue permissions. Existing tickets remain unchanged; qualifying open tickets may receive a link. Several later reports can refer to the same earlier repair.

  • A recurring Floor 2 air-conditioning problem is a new report with its own reporter, description, work notes and eventual resolution.
  • The technician explicitly selects one earlier resolved ticket at the same location and explains why they are related. Multiple later occurrences may refer to that repair.
  • While the new ticket is open, a technician may correct or remove a mistaken link, retaining who changed it, when and why.
  • On resolution, protect the relationship. The earlier ticket stays resolved and keeps its original notes and resolution.
  • Do not reveal another reporter's title, notes or resolution through a link. Show cross-ticket content only to readers authorised for both tickets; technicians use their existing queue permissions.
  • Existing tickets remain unchanged when the feature arrives. Qualifying open tickets may receive a link; resolved tickets may provide earlier context.
  • An illustrative code-review finding is a related preview that exposes another reporter's record. Correct it without overwriting either occurrence's history.
Read the revised CanLang source and version coverage
06Your company · Requirements and next steps

Make the rules your own.

Use this example to agree a first useful workflow with its owner and technical counterpart. Identity, purchasing, inventory and other connections need their own evaluation.

Locations
How are sites, buildings and areas recorded?
Reporting fields
What does a technician need to begin investigating?
Authority
Who can append work, link reports and resolve tickets?
Priority
Which priority conventions help the queue?
Work evidence
What should each note retain?
Resolution
Define resolution and separately evaluate scheduling, assets or notifications.

Explore another workflow · Read the factory handbook · Get help