~/progetti/donumai/README.md
~/progetti/donumai/README.md

donumAI

{ date: “2026-08-08”, status: “in development”, stack_modules: 14 }

Stage engine for software delivery — the agents write, but nothing moves forward until you say so

donumAI came out of a very specific irritation: tools that promise to 'write your project documents with AI' produce plausible text and no way of knowing whether it is still true. An architecture document generated in March sits there, as convincing as on day one, while in May the requirements have changed and nobody notices. donumAI starts from the opposite assumption: the hard part is not generating, it is holding together six documents that contradict each other the moment the project moves. The product is an engine that walks a software project through six stages — discovery, estimation, domain design, architecture, implementation plan, test plan — and at every handover puts a person in front of what the agents wrote.

## The six stages and the gate

Each stage produces a versioned artifact made of sections. The agents write it, but they do not close it: review happens section by section, and for each one you can approve, comment, or request changes. A stage unlocks the next only when its gate is approved — that is not a formality, it is a real lock. It is the difference between an assistant that produces and a process that advances.

·Discovery: the initial brief, gathered through an interview that runs in phases
·Estimation: the numbers and the assumptions they rest on
·Domain design: the model of the business domain
·Architecture: the locked decisions, together with the alternatives that were discarded
·Implementation plan: epics, sequencing, milestones, definition of done
·Test plan: strategy and distribution of the tests

## When something upstream changes

The bet behind donumAI is narrow and mechanical: version every document, record which decision each part came from, and the moment an input changes, mark as stale everything downstream that depended on it. Drift does not go away — it stops being invisible, which turns out to be most of the problem. Trace links are not inferred by a model: they come from a projection over the data, because an inferred link carries an error rate, and from that point on the whole chain carries it too.

## Deterministic rules before rubrics

Every artifact passes through a validation layer before it reaches the gate. The principle: any property that can be captured as a typed field leaves the judgement of a model and becomes a structural rule — presence, cardinality, type, membership of a closed set. Structural rules cost zero calls, do not vary between runs, and cannot be talked round by well-written prose. Only what is left — the irreducibly semantic judgement — goes to the rubric.

·For the architecture stage alone: 154 deterministic rules, extracted line by line from the method documents
·Three severities that never get collapsed into one: block stops the gate, repair triggers an automatic correction loop, warn shows up at the gate without blocking
·Closed enums copied verbatim from the sources, because a model's prior knowledge is frequently out of date
·Mandatory verdicts on the properties that do require judgement but where the judge can be forced to cite its evidence

## Markdown is never an input

This is the invariant that holds everything else up, and it is also the technical decision I am most attached to. The worker does not produce text: it produces typed objects, through a forced tool call. The rules run on the objects. Markdown is a projection, produced by a pure renderer, and no component downstream ever reads it. If I catch myself writing a parser or a regex over prose in this project, I have gone the wrong way. An extraction layer is an inference with an error rate, and every rule downstream would inherit that error and stop being deterministic. There is even a test that checks the invariant against the AST — by return type, not by function name — and that proves it is not blind by picking up the legacy parsers still sitting in an older module.

## The method guides

Behind each stage there are method documents, and those documents are readable inside the product itself: a study section explaining why an artifact looks the way it does, what makes a decision lockable at all, and the criteria it will be judged against. They are written in Italian while the rest of the interface runs in English, which turned typography into a real problem: long Italian prose has a different measure and a different rhythm from a dense interface.

## Design and visual identity

The interface is built for technical people who spend hours inside it: dense tables, long documents, gate decisions. Density and legibility before decoration. The system is called 'Slate & Ferrous' and is near-monochrome with a single blue accent: near-black marks the primary actions, blue marks state — so a board full of cards stays readable without colour doing two jobs at once.

·Palette in OKLCH, light and dark both designed rather than one being the inverse of the other
·Typography: Bricolage Grotesque for headings, Public Sans for the interface, JetBrains Mono for identifiers, versions and metrics — if it is monospaced it is by definition a machine value
·Self-hosted fonts: no third-party requests on the public pages, which is also the only way to write a cookie policy without a banner and still be telling the truth
·The mark is a double hexagon with a filled node on each of the inner ring's six vertices: one per stage

## Tech stack

The project is a TypeScript monorepo where the control plane and the execution plane are deliberately kept apart: the API takes requests and owns the state, while a separate worker runs the jobs, which can take minutes.

·Control plane and engine: NestJS with Prisma and PostgreSQL
·Execution plane: a separate worker on BullMQ and Redis
·Web: React 19, Vite, TanStack Router, HeroUI, Tailwind CSS v4
·Shared contracts: Zod schemas in their own package, used by both the API and the client
·Provider-agnostic LLM client, isolated in its own package
·Monorepo managed with pnpm

donumAI is a personal project, developed alone, and it follows an order I imposed on myself: first the typed wiring, then invoking the judge and measuring its variance, then everything else. The 154 rules written so far remain hypotheses until they have run on artifacts that were actually generated, and the table I care about has two columns: the rules that never fire and the rules that always fire. Their diagnoses are opposite and have to be read together — a rule that always fires is not a success of the validation layer, it is a symptom of something to fix further upstream.

// stack
[nestjs”, prisma”, postgres”, redis”, bullmq”, react”, vite”, tanstack-router”, typescript”, tailwind”, zod”, llm”, monorepo”, sdlc”, ]