Built and wired · three engines are written but not switched on
The building estate, run on a workflow that cannot be gamed — not a spreadsheet and a shared inbox.
Facilities.software is live: the work-order board, the preventive-maintenance clock, the equipment nameplate, the vendor directory, the capital-plan rollup, room and facility booking, the district asset/inventory ledger, and the three-party venue-reservation approval all persist to a real database when one is configured, and 503 honestly when it is not. Three adjacent modules -- energy-meter monitoring, a work-order parts-and-labor cost rollup, and a vehicle fleet safety-inspection log -- are written and tested and have no route wired to them. We say which is which on this page, not just in a changelog.
The one legal path: open → assigned → in progress → done
criticalGym heater is outassigned
highRoom 214 window will not latchin_progress
mediumCafeteria walk-in fridge running warmopen
lowHVAC filter, wing B (PM due)done
Illustration in the exact shape the engine returns. Every status here is one legal step past the last; nothing skips a stage and nothing leaves “done.”
What this is, and what it deliberately is not
A district's building estate usually runs on whatever survived the last staff turnover: a shared spreadsheet for open repairs, a paper binder for warranty dates, a group text for the HVAC contractor. None of that has a workflow anyone can point to and say “this is the rule.” Facilities.software is the operations spine underneath that estate — a work-order board with exactly one legal path, a preventive-maintenance clock that does the arithmetic instead of a sticky note, an equipment record that outlives the person who remembered the warranty date, a vendor directory with an actual gate on who a job can go to, room and facility booking that cannot be double-booked, a capital-plan rollup that turns “everything needs attention” into a number, a district-wide property ledger, and a three-party approval for venue reservations that carry a cost.
It is deliberately narrower than the category's biggest incumbents in three specific ways, named here rather than left for a pilot to discover: it does not dispatch a technician or place a call; it does not integrate with building-automation hardware or an IoT sensor network; and it does not currently offer a mobile app. Each of those is a real gap, not a soft one, and the comparison table below states them outright.
Worried this is a rebadged ticketing tool with a facilities label stuck on it? It isn't — the nine engines share one Postgres schema keyed to the same tenant, asset, and location ids, so a work order, a PM interval, and a capital-plan line about the same boiler are the same row, not three disconnected tools that happen to sit on one domain. There is no CSV export, no SFTP feed, and no API key issued for outbound integration anywhere on this page today — a business office that still lives in a spreadsheet, or a district that already maintains its vendor list on an SFTP server, has no automated bridge into this data. That is a real limit, named here rather than discovered in a procurement call.
The nine engines that are actually wired
Each of the following has a route a Fastify handler calls and a store that persists to a real table when a database is configured — and 503s honestly, with the reason no_db, when one is not. None of it is a mock.
Work orders
The work-order board — a fixed, one-way workflow nobody can game
Someone reports a problem at a place or on a piece of equipment: the gym heater is out, room 214 has a broken window. A work order records WHERE (an opaque asset or location reference, never a person), WHAT (the issue, in plain language), and HOW URGENT (low, medium, high, or critical), and then walks exactly one legal path: open → assigned → in progress → done. Every other move is refused. You cannot mark a job done before anyone has picked it up. You cannot quietly re-open a finished job — done is a terminal state, and no edge leaves it. An unknown status string is never honored. Two people reading the same order always agree on what may happen next, because the workflow is a closed law, not a convention.
back-office-facilities-work-orders.tsPersisted to a real work_order table. No database configured -> an honest 503, never a fabricated board.
Preventive maintenance
The PM clock — some upkeep is on a schedule, not a complaint
Flush the boiler every ninety days. Change the HVAC filter every thirty. A preventive-maintenance schedule states three facts — the asset, the interval in days, and the day it was last done — and the next-due day is a pure, repeatable calculation: lastDone + intervalDays. Ask “what is due as of today” and you get the same answer on every machine, every time. A malformed interval or a bad last-done day is refused outright rather than silently coerced into a guess, and a schedule that fails that check is skipped when the due list is built — a broken schedule can never trigger phantom work. The due list itself returns in a stable, deterministic order: ascending due day, ties broken by schedule id.
back-office-facilities-work-orders.ts (nextDue / pmDueList)Persisted to a real pm_schedule table, same store and same honest-off 503 as the work-order board.
Equipment nameplate
The equipment nameplate — the record a work order never carried
A work order's target is deliberately just an opaque reference — it carries no make, model, serial number, warranty date, or replacement cost. The asset master is the genuinely missing nameplate: one record per asset, keyed by the same opaque assetRef a work order or a PM schedule already uses, so a technician staring at a broken boiler can pull its warranty status and its planning-level replacement cost without a second system. Warranty status is computed against a caller-supplied “today,” never an ambient clock, so the calculation is exactly as testable as everything else on this page. The replacement-cost figure is a planning estimate a human enters — it is never a charge, and no money moves anywhere in this module.
back-office-facility-asset-master.tsPersisted to a real facility_asset table (migration 1189). No money moves; the cost field is planning-only.
Vendor & contractor directory
The vendor directory — who is allowed to touch a job, decided in one function
A district registers the outside contractors it works with — a business name, an opaque contact reference, and an active flag — and a job can be handed to one through a single, fail-closed decision: is the actor facility staff, is the vendor active, and is the work order still open. An inactive vendor is kept on file, not deleted, and a job already marked done cannot be dispatched to anyone. This module never touches the work-order status law itself — “who is working this job” and “what stage the job is at” are two different facts, deliberately kept in two different engines, joined only by an additive, optional column.
back-office-facility-vendor-directory.tsPersisted to a real facility_vendor table (migration 1190). No dispatch call, text, or email is sent anywhere.
Capital & deferred-maintenance rollup
The capital-plan rollup — the number a board can actually act on
A facilities director should not have to re-read every open work order and every overdue PM entry by hand to answer “what does next year's capital budget need to cover.” This is a pure reporting layer — it creates no new persistence and models no new fact — that aggregates three things a caller already has: the open work-order board broken down by urgency, the PM due list, and (optionally) the asset master's replacement-cost map. An open work order carrying an unrecognized priority is simply excluded from the count, never miscounted into an existing bucket, and the deferred-cost total is zero whenever no cost estimate is on file — never a fabricated number standing in for one that was never entered.
back-office-facility-capital-plan.tsNo new persistence by design. It reads what the WO/PM/asset engines already store and computes.
Room & facility booking
Room and facility booking — a gym cannot be double-booked, by construction
A school owns bookable resources — rooms, gyms, fields, auditoriums, equipment — and staff hold one for a time window. The booking check is double-booking fail-closed: a requested window that overlaps any live booking on the same resource is rejected outright and nothing is held, using the same half-open overlap predicate the rest of the platform's scheduling core runs on. Back-to-back bookings — one ending exactly when the next begins — do not conflict, and a malformed time window fails closed rather than being interpreted charitably. A cancelled booking frees its window immediately; a held or confirmed booking occupies it.
back-office-facility-booking.tsPersisted to real bookable_resource and booking tables (migration 0597). No PII: a requester is an opaque ref.
Community facility rental
Renting a facility to the community — the same booking, plus an honest fee
A community rental is an ordinary booking with one thing layered on: an exact-integer-cents fee in the platform's closed usd/cad currency set, charged through the shared payment rail. Absent a provisioned payment provider — which is the default, and every test — the rental comes back queued_not_charged with a null transaction id. Not a fabricated receipt, not a silent success. Renting a facility to the community is gated to facilities/booking staff before anything else runs.
back-office-facility-booking.ts (CommunityRental + payment-rail.ts)queued_not_charged with no transaction id until a real payment provider is wired in. No money moves today.
District asset & property inventory
The property ledger — acquire, assign, transfer, retire, and prove it happened
Distinct from the equipment nameplate above: this is the district-wide property estate, not just the assets a work order references. A property-tagged item is acquired, moves through in_service, assign, transfer, and store, and eventually retires — a fail-closed lifecycle where retired is terminal, mirroring the same discipline as the work-order status law. The board reads on-hand inventory and a by-location count rollup with retired items excluded, and one asset's full history replays deterministically from an append-only ledger — the same input sequence produces the same history, every time. An assignee is an opaque staff or room reference, never a student.
back-office-asset-inventory.ts (packages/shared) + routes/asset-inventory.tsPersisted to real tables (migration 0596). Asset-manager role required, re-checked in the route as defense in depth.
Venue reservation approval
Three-party venue approval — facilities, admin, and the budget owner, all three or none
A venue reservation with a cost runs on the platform's own business-process substrate: a real process state (draft, submitted, approved, or a rejected/cancelled sink) advanced only by the engine's own transition function, with the three required sign-offs — facilities, a school admin, and the budget owner — expressed as process variables a pure gateway reads. The engine's single approve action fires only once all three are true and the run is still submitted; an earlier sign-off moves nothing on its own. The cost is captured as exact integer cents at submission and, on approval, is encumbered against a budget line — it is never charged, paid, or disbursed. Every action appends one immutable audit entry, and replaying the log reproduces the exact same trail.
mosaic-facility-approval.ts (on the shared mosaic BPM engine)Real persisted process run. Encumber-only: the engine's pay stage is structurally unreachable in this module.
The preventive-maintenance clock, worked through
Two schedules, same deterministic mathDay
Boiler flush intervalRuns every 90 days90 days
Last doneDay 210 of the school-year clockday 210
Next duelastDone + intervalDays, exactlyday 300
HVAC filter, wing B30-day interval, last done day 275day 305
Illustration with round example day numbers, using the day-index clock the engine actually
runs on (a whole, caller-supplied day count, never a wall-clock read). Ask “what is due as of today”
and the answer is the same on every machine, because it is addition, not a guess.
Built, not switched on
Three modules in this codebase are written and tested and have zero apps/ importers today — no route, no store, not registered anywhere a request could reach them. We would rather name them here, by name, than let a green test suite be mistaken for a working feature.
Module
What it does
Why it is not switched on
Energy-meter monitoring
Turns a series of raw meter readings into per-period consumption deltas and flags a period as an anomaly when it exceeds a supplied baseline and tolerance. A reading lower than the one before it is flagged as a rollover or a fault, never reported as negative usage. Descriptive arithmetic only — no model, no prediction, no learning.
No route, no store, and no import anywhere under apps/. There is no meter feed to read from and nowhere the numbers land yet.
Work-order parts & labor cost rollup
Sums parts (quantity times unit cost) and labor (minutes times an hourly rate, floored with the remainder surfaced) plus a flat-and-basis-point markup into a per-work-order total, and can split that total across cost centers by exact largest-remainder apportionment.
No route, no store, and no import anywhere under apps/. The work-order board above carries no cost fields today.
Vehicle fleet safety-inspection log
Tracks a fleet vehicle by an opaque asset reference and makes an overdue safety inspection dominant, fail-closed, over any mileage-based service signal: an inspection-overdue vehicle is out of service and dispatch is blocked outright, regardless of how current its mileage service is.
No route, no store, and no import anywhere under apps/. This is the honest answer to “does this cover inspections” for a vehicle fleet — correct, and not switched on for a building estate.
Data, access, and money — the honest posture
No student data
A work order names a heater or a room, never a child. Every reference on this page — asset, location, vendor, requester, assignee — is an opaque, in-tenant identifier for a place, a thing, or a staff member. That is why these tables sit outside the student census wall entirely, rather than inside it under a special exception.
Facility staff only
Every write path checks the same role gate: school or district administrator, with a commissioned sales rep hard-denied first, before any allow-list check runs. A teacher, parent, or student has no path into any board on this page. The asset/inventory route re-checks the same gate a second time in the handler itself, as defense in depth.
Tenant-isolated by default
Every persisted operation runs inside a per-request tenant-scoped transaction. A missing or unreachable database resolves to an honest 503 with the reason no_db — never a fabricated empty board standing in for a real one.
No money moves, with one honest exception
Replacement-cost estimates, capital-plan totals, and (when it is ever wired) a work-order cost rollup are all planning numbers, never a charge. The one path that touches money — a community facility rental fee — is honest-off by default: absent a configured payment provider it returns queued_not_charged with a null transaction id, never a fabricated receipt.
No AI, anywhere
There is no model, no prediction, and no assistant wired to any of this data. The energy-monitoring module's own header states plainly that it is descriptive arithmetic, not AI — a delta is subtraction and an anomaly is a threshold comparison against a supplied number, nothing learned or inferred.
Fail-closed, not fail-plausible
A malformed PM interval, an unknown work-order status, an overlapping booking window, or an unrecognized priority string is refused or excluded outright rather than coerced into a guess. A broken schedule can never trigger phantom work; a bad booking can never silently double-hold a room.
Against the category, including where we lose
What matters
The category
Facilities.software
Dispatches a technician and tracks a live truck roll
Yes. Live dispatch, mobile crew apps, and route tracking are standard advertised features of the category.
No. The vendor directory decides whether a hand-off is legal. Nothing places a call, sends a text, or tracks a truck.
Integrates with building-automation (BMS) hardware and IoT sensors
Yes. A marketplace of BMS and IoT integrations is a standard advertised feature of established platforms.
No. There is no sensor feed and no BMS integration. The energy-monitoring arithmetic exists and has nowhere to read from yet.
A mobile app for technicians with photo attachments in the field
Yes. A dedicated mobile app is a standard advertised feature of the category.
No mobile app exists today. Every surface on this page is a server-rendered web route.
The status workflow is a closed law, not a set of configurable stages
Not established. Configurable workflow stages are common in the category; whether any given one is closed and fail-closed is not something we can verify from outside.
Yes. Open, assigned, in progress, done — four states, one legal forward edge each, done is terminal. Nothing configures around it.
A double-booked room is structurally impossible, not merely warned against
Not established. Conflict warnings are a common category feature; whether a booking can still be forced through is not something we can verify from outside.
Yes. An overlapping window on the same resource is rejected before anything is held. There is no override path in the engine.
A venue reservation with a cost requires three independent, named sign-offs to move
Not established. We cannot inspect another product's approval model and will not guess at it.
Yes. Facilities, a school admin, and the budget owner must each sign off; the approval action is unreachable until all three do.
What happens when a preventive-maintenance schedule is malformed
Not established. We cannot see another provider's behaviour in this case, so we do not describe it.
It is skipped from the due list entirely. A broken schedule can never trigger phantom work.
Every module the vendor is not ready to switch on is named as such, in public
Not established. We do not audit other vendors' roadmaps and will not characterise them here.
Yes. Three written and tested modules with no route wired to them are named on this page, by name, as built-not-switched-on.
“The category” means the established commercial CMMS/IWMS (computerized maintenance management / integrated workplace management) software market. We have not audited any specific vendor's source, so every row above states either an openly-advertised category capability or an explicit “not established” — never a guess at a competitor's internals.
What a conversation about a pilot looks like
Tell us which engines you need on day one
Most districts start with the work-order board and the PM clock; room booking and the capital-plan rollup usually follow once the first two are in daily use.
We confirm what stays out of scope
No dispatch, no BMS/IoT integration, no mobile app, no AI. If any of those is a hard requirement for your facilities office today, we will say so plainly rather than let a pilot discover it.
We talk about the three built-not-switched-on modules
Energy-meter monitoring, the work-order cost rollup, and the vehicle fleet safety-inspection log are written and tested. Wiring any one of them up is a real, scoped piece of work, not a flag flip — we will tell you honestly what that takes.
We talk about migrating what you already have
Open work orders and equipment records move over as a scoped, human-reviewed migration — there is no built-in importer or self-serve sync on this page today, and we would rather say that plainly than promise a one-click migration nothing in this product currently performs. How long it takes depends on how much data you have and how clean it is; we size that with you before we start, not after.
What if your facilities office already runs a ticketing tool it likes? We won't ask you to rip it out on day one — a pilot can run the work-order board and PM clock alongside an existing system for a grading period. What if the answer is "no" after the pilot? That happens; we would rather learn it in a scoped pilot than in a signed multi-year contract.
Questions
What does facilities.software actually do, in one sentence?
It is the operations spine for a district's physical estate: a work-order board with a workflow that cannot be gamed, a preventive-maintenance clock, an equipment record, a vendor directory, room and facility booking with a hard double-booking refusal, a capital-planning rollup, a district-wide property ledger, and a three-party approval for venue reservations with a cost. It records and computes; it does not dispatch a truck or talk to your building-automation system.
Does it dispatch a technician or send a text when a work order comes in?
No. The vendor directory answers a narrower question — may this specific job legally be handed to this specific vendor, right now — and it answers with a decision, not an action. Nothing in this product places a phone call, sends a text message, or emails anyone. If your process depends on an automatic notification firing, that step is a manual one today.
Can it double-book a gym or a room?
Not through the engine. A requested booking window that overlaps any live booking on the same resource is rejected before anything is held, using the same overlap check the rest of the platform's scheduling core runs on. Back-to-back bookings are allowed; overlapping ones are not, and there is no override path inside the function that makes the decision.
Does it charge community members who rent a facility?
Only if a payment provider is actually configured, and none is by default. Absent one, a community rental comes back queued_not_charged with a null transaction id — never a fabricated receipt and never a silent success. The fee itself is exact integer cents in a closed usd/cad currency set, computed the same way whether or not a provider is wired in.
What happens to a venue reservation that has a cost attached?
It requires three separate, named sign-offs — facilities, a school admin, and the budget owner — before the approval action can fire at all. On approval the cost is encumbered against a budget line: it is set aside, not paid. This product has no pay action anywhere in that workflow; disbursing the money, if that is ever wanted, is a different system's job.
Does it cover safety inspections?
Partly, and we would rather say exactly which part. The preventive-maintenance clock is the general mechanism a scheduled inspection would run on — an interval, a last-done day, a deterministic due date. Separately, there is a written and tested vehicle-fleet module whose headline rule is correct and worth knowing about: an overdue safety inspection takes a bus out of service and blocks dispatch outright, ahead of any mileage signal. But that module has no route wired to it today, so it is not a live feature of this product — it is named on this page as built-not-switched-on, not quietly implied to be working.
Is there a mobile app for maintenance staff?
No. Every surface described on this page is a server-rendered web route behind a role gate. A crew member checking a work order from a phone today is using the same web page a desk would use.
Does an assistant or an AI feature answer questions about the facilities data?
No. There is no model, no prediction, and no assistant wired to any of this data anywhere in this product. The energy-monitoring module's own source header states plainly that it is descriptive arithmetic, not AI, and that posture holds across every engine on this page.
Is there student data anywhere in this?
No. A work order names a heater or a room, never a child. Every reference on this page — asset, location, vendor, requester, assignee — is an opaque, in-tenant identifier for a place, a thing, or a staff member. That is the specific, narrow claim: not “we hold no data,” but that the data these engines hold has no student identity in it, which is why this module sits outside the student census wall rather than inside it.
Who can open, assign, or close a work order?
Facility staff only — school or district administrators, by the platform's canonical role vocabulary. A commissioned sales rep is hard-denied before any allow-list check runs, mirroring the same rep wall used everywhere else in the platform. A teacher, parent, or student is not facility staff and has no path into this board.
What happens if my district has no database configured for a given engine?
The route returns an honest 503 with the reason no_db. It never falls back to a fabricated empty board or a plausible-looking default. Every store on this page follows the same rule: persist for real, or refuse to pretend.
How is the equipment record different from the district asset/inventory ledger?
They answer different questions and are deliberately two separate modules. The equipment nameplate is the make/model/serial/warranty/replacement-cost detail for the specific assets a work order or a PM schedule already references. The asset/inventory ledger is broader: the whole district property estate moving through acquire, in-service, assignment, transfer, storage, and retirement, with a full replayable history. A boiler can appear in both; a box of laptops that never generates a work order lives only in the second.
What does it cost?
There is no price on this page and no checkout anywhere on this site. The honest next step is a conversation about which of these engines your facilities office actually needs on day one, and which of the three built-not-switched-on modules would be worth wiring up first.
Is facilities.software a school, a district, or a nonprofit?
No. It is a for-profit software product built by Stanley Studios. It is not a school, not a district, and not a nonprofit, and nothing here is a charitable gift of any kind.
Ask about a pilot
Tell us which engines your facilities office needs first, and we will tell you honestly what is wired, what is not, and what a real timeline looks like.
If a live dispatch network, a BMS integration, or a mobile app is a requirement, the honest answer is above — we would rather you read it before a call than during one.