An idea I want to prove. Planning, scheduling, forecasting, constraints, geometry — construction runs them as five separate systems. I think they are one thing wearing five costumes. This page is my attempt to demonstrate that with a working prototype: one primitive, two link types, one derive step. Every atom is a constraint. Every constraint is an atom. Read the theory, then try the engine — everything below is live and runs on the same fifty lines of math.
Every project-controls stack today runs on five separate systems: a scheduling tool (usually Primavera P6 or Microsoft Project), a work-package tool, a materials system, an RFI log (request for information — questions to the design team that block work until answered), and a risk register — each with its own database, its own vendor, its own integration bill, and its own version of the truth. Reconciliation is a full-time job, and the reconciliation is usually wrong.
ACE keeps one table. Every row is an atom. Every relationship is one of two links. Every question you can ask about a project — is it ready? what's on the critical path? what will it cost? what happens if we slip? — is a walk on that graph. The engine is fifty lines. The rest is discipline: refusing to add a second primitive when someone waves a whiteboard at you.
requires edge, that's itEvery atom is a constraint. Every constraint is an atom. That is the point. A task is a constraint on the schedule. An RFI is a constraint on a work package. A material delivery is a constraint on install. A regulatory hold is a constraint on start. There is no separate constraint table — the primitive is one thing. The type tag tells you which face of that thing you're looking at. Same seven fields for all of them.
The kind field on an atom answers one question: who declares it done? There are five. Not four. Not seven. Anything you want a "task" or a "constraint" to be is one of these five, wearing a different type tag.
narrative-required.contains is done, it's done. No separate progress field.The first link is a tree — a work-breakdown structure (WBS). A project contains areas. Areas contain work packages. Work packages contain tasks. Every atom has exactly one parent. Rollups climb the tree from leaves to root. No many-to-many. No secondary breakdown. If you want a matrix view, that's a tag, not a link.
The second link goes sideways. Formwork requires rebar. Pour requires formwork. Cure requires pour. This is where "ready" lives. An atom is workable when everything it requires is done — no separate constraint system, no "5 categories of readiness". Just walks the graph.
No cycles allowed. Ever. The engine refuses to save a graph that would deadlock.
Mark a leaf done and watch the graph. Its parent's done state is never written anywhere. It's a function: done when everything I contain is done, and everything I require is done. Walk the graph, resolve each atom, loop until nothing changes. Because the graph is acyclic — a DAG (directed acyclic graph, no loops allowed) — one pass suffices.
Some atoms are tagged narrative-required (marked with a ★): a regulator or a QA signature demands a written record of how it went. Click one of those below and a canned-response modal opens — pick a match or type your own. Without a narrative entry, the graph won't close that atom. This is where the model refuses to lie for you.
narrative-required) and a canned-response modal opens. Pick a match or type your own. The journal below fills in as the audit trail — you cannot close a required atom without one.The critical path method (CPM) is standard planning math: the longest chain of dependent work from start to end. Every task on that chain has zero float — slip any one of them by a day, the whole project slips by a day.
Here's the interesting part: the links that answer "is this work ready?" already answer "what has to come first?" Put a duration on each atom, walk the requires graph, and the dates and the longest chain fall right out. Same graph. No separate P6 file, no reconciliation, no midnight import-error emails.
Every work atom has three durations: [min, likely, max]. Sample a triangular distribution, walk the graph, record a finish date. One thousand runs. Read the histogram — P50, P80, P90 (the 50th, 80th, and 90th percentiles: half your simulated runs finish by P50, 80% by P80, 90% by P90). The plan isn't a date; it's a distribution.
Triage is a query, not a table. It walks the graph every morning and answers four questions in order:
Red — atoms on the critical path that aren't ready. Amber — atoms one hop off the critical path with a floating constraint. Blue — new atoms with no parent yet (the whiteboard's contribution to the graph). Green — quietly workable, no attention needed.
Nothing gets its color stored anywhere. Colors are derived.
Earlier I wrote that flex atoms have no requires. That was wrong. Every atom is a constraint. Flex is no exception. A flex package still needs a crew with the right competency (requires: a rotating "any-idle-crew" atom). It still needs the physical zone unlocked (requires: zone access). It still needs materials on hand (requires: on-shelf inventory). What makes flex flex isn't the absence of constraints. It's the shape of the constraints: they're capacity constraints, not sequence constraints.
The distinction is real and mathematical. A sequence constraint (requires: REBAR) points at a specific atom by id. A capacity constraint (requires: any(crew where competency=civil, status=idle)) points at a predicate — a set. Same requires link; the target is a query instead of a name.
That's why flex packages absorb schedule variance: their gates fire on availability, not on completion. The engine treats them identically to every other atom during derive.
Construction runs on procedures — Word documents, PDFs, laminated cards taped to walls. They read as prose. They never talk to the schedule, the RFI list, or the material log. When something is late, nobody reads the procedure. When the procedure is wrong, nobody updates the atoms.
ACE fixes this by making the procedure be the atom graph. Every noun in the prose — every step, every tool, every material, every check, every witness — is a live atom pill. Hover it and the right rail shows its state. Click and it opens in the workbench. When a material atom flips to done, the pill in the procedure turns green. When an RFI comes back and clears a gate, the pill turns blue. The procedure reads exactly the same. But now it runs.
The same primitive that carries schedule and rollup carries space. Every building on a site is an atom. Its position isn't a stored coordinate — it's an expression, evaluated from a handful of dimensions. The reactor building has a diameter. The turbine building sits 15 m east of the reactor edge. The cooling towers sit 20 m south of the turbine, spaced by ct-sp.
Drag the sliders. Every atom's coordinates re-solve. The 3D view is generated the same way — same expressions, isometric projection.
The whitepaper claims one primitive. That's a strong claim. The obvious pushback is: surely some things aren't atoms. I built this taxonomy by asking, for every noun a construction organization uses, "would treating it as an atom break anything?" The answer, so far, is no.
Nine families. Seventy-two atom types. Each one gets seven fields, two link types, one derive. The engine doesn't know the difference between them.
contains and requires. The variation is in what the fields mean. A weather atom's duration is a forecast window. A hazard atom's duration is exposure time. A commitment atom's duration is time-to-fulfillment. The engine doesn't care.The taxonomy above is 72 types. Every one fits. That should make anyone suspicious — the model is too accommodating. So let me try to break it. Pick the hardest cases. Prove there's a thing that can't be an atom. Then read the disproof. Then pick a harder case.
I stopped at round eight. I don't think there's a round nine, but I'd love to be wrong.
contains, requires) are relationships and they're first-class in the schema. If you want a labeled relationship ("blocks", "duplicates", "co-signed-by"), give it its own atom type, put the two endpoints in its requires list, and now the relationship carries its own state, its own narrative, its own audit trail. Every relationship is an atom looking sideways.narrative. The claim conflates "a moment" with "the number denoting the moment". The number is a field. The moment is the atom.kind is manual and its tags include narrative-required. It gets updated when the two people work together on other atoms — you write about the work, and the trust atom's narrative accrues history. Its "duration" is the time to rebuild after a breach. Its "requires" is a series of small commitments kept. This is why work relationships are engineerable.kind: manual and type: assumption. It has a manualDone: true until someone challenges it — at which point it flips to manualDone: false and every atom that requires it becomes not-ready. Assumptions are real: they are load-bearing in the graph. The whole schedule is built on them. The engine's job is to know which ones are being trusted and by what.void atom explicitly noting a decision not to do something (a de-scoped work package, a rejected change order). Both are atoms. What isn't in the graph is still describable by the graph — the shape of the hole is data.ruleset atom in the meta-graph. Its derive function is a procedure atom. Its contains field points at the type registry, its requires field points at the axioms it depends on ("no cycles", "finite atoms", "acyclic requires"). The settings that configure ACE are atoms. The schema is an atom. The engine describes itself in its own language. That's how you know the language is complete.false. Every view is an atom. So the concept of a thing that isn't an atom is itself an atom pointing at an empty set. The claim eats itself.The demos above and the workbench below share the same ACE instance in memory. Every command you type here is the actual application programming interface (API). Every mutation re-derives the graph.
help for the full command list, or click a chip.Same math. Same atoms. Same derive. A two-and-a-half minute rendered composition where every frame is pure(frame) → pixels. Watch it below, or scrub through the scenes with the transport.
You read the theory. You watched the graph resolve itself. You ran a thousand Monte Carlo scenarios. You dragged the parametric sliders. You clicked through the literate procedure. You typed real commands into a real shell. Every one of those interactions was the same seven-field record and the same fifty-line derive step. That is the entire pitch.
stats in the workbenchEverything the engine exposes. Everything you can put in your project file. Copy-paste from here.