Measuring progress
How a project's percentage is computed from packages and tasks, how health and freshness are worked out, and why none of it is stored.
No percentage above the task is stored in this service. Every project and package figure is computed at read time from rows somebody can go and look at. That is a deliberate constraint, recorded in docs/decisions/0088-measurable-progress.md and amended by docs/decisions/0092-planning-and-document-control.md, and it is what separates a progress figure from an opinion.
A stored project percentage has no answer to "why". Somebody typed 70 into a box, and a month later nobody can say which tasks they were counting or whether the two they forgot would have made it 55. A computed percentage always has that answer, because the only way to change it is to change a task, or file a dated report with a name on it.
Three levels
| Level | How its percentage is arrived at |
|---|---|
| Task | Set by the person doing the work, from 0 to 100. A done task counts as 100 whatever it was set to, and a cancelled task is left out |
| Package: A named body of work inside a project, such as signage, facade, or amenities. It is measured either by the progress of its tasks or by a percentage somebody reports. A project's progress is the weighted average of its packages and is never stored. | The weighted average of its tasks' progress, or the newest progress report filed against it |
| Project | The weighted average of its packages, or of its linked tasks when it has no packages. A project with neither reads 0 |
A package's measure is fixed when it is created and is one of two values.
tasks means the percentage is sum(weight × progress) / sum(weight) over the tasks
linked to the package. A package with four equally weighted tasks, one done and one at
50%, reads 37.5%. A cancelled task leaves both sums: cancelling work does not make the
rest of it more complete, it makes there be less work.
reported means somebody files a figure with a date, and the newest one wins, ordered by
the date the figure describes rather than the date it was entered. A report filed today
about last Tuesday does not overwrite one filed last Tuesday about today. Reports are
never edited; a correction is a new report.
Both figures are database views in the same database, project_package_progress and
project_progress, in services/construction/migrations/0003_task.sql.
A task that counts toward several projects
A task exists once and can link to more than one project. One link is the primary one; the others say the task affects that project, is shared with it, or is a deliverable for it. Each link can carry an allocation percentage, and the views multiply the task's weight by it. A shared task allocated 40% to one project and 60% to another counts 40% of its weight in the first and 60% in the second, so the same piece of work is not counted twice at full weight.
What a project percentage means
A project's figure is the weighted average of its packages. The weights are the point: a project whose facade package carries weight 3 and whose signage package carries weight 1 reads 75% when the facade is done and signage has not started, not 50%.
A project with no packages and no tasks reads 0%, and that is not a bug. It means nothing is being measured, which is different from nothing having happened and also different from being finished. The console says so in those words rather than showing a bare zero.
Health
Health is computed in Go when a project is read, in services/construction/internal/domains/project/health.go. The first rule that applies wins.
| Health | When |
|---|---|
complete | The project is completed |
off_track | The forecast finish is after the baseline finish (or after the planned finish, before there is a baseline), or the current stage's planned finish has passed and the stage is not complete |
at_risk | A task linked to the project is late or blocked |
on_track | None of the above |
A task is late when it is not done or cancelled and its forecast finish has passed, or its planned finish when it has no forecast.
Freshness
Project managers post a weekly update: a note, a forecast finish, and any blockers. Updates are due on Thursdays. Freshness reads the newest update:
| Freshness | When |
|---|---|
current | There is an update since the most recent Thursday |
update_due | The newest update is from before the most recent Thursday, but within 14 days |
stale | The newest update is more than 14 days old, or there is none |
Health says whether the project is going well. Freshness says whether anyone has looked at it recently. A project that reads on track with a stale update is a project nobody has checked, and the portfolio shows both side by side for that reason.
Milestones
A milestone has a planned date, an optional forecast date, and an actual date once it is
hit. Completing one publishes project.milestone_completed with how many days late it was
against the forecast, or the planned date when there is no forecast.
A milestone can name another project that drives it, for example a ground-up building's "site ready" milestone driven by the master plan's infrastructure project. When the driving project is completed, every open milestone it drives gets its actual date in the same transaction.
Dependencies
A task can depend on another task, finish to start. Two rules are enforced when the dependency is created or when a status changes:
- A task cannot be marked done while a task it depends on is unfinished. The refusal is
409
unfinished_prerequisite. - A dependency that would complete a cycle is refused with 409
dependency_cycle.
A task can also require a Controlled document: A document whose changes are tracked formally, such as a drawing, a specification, or the project schedule. It keeps one permanent code for its whole life; a change adds a revision rather than a new document.. It cannot be marked done
while a document it requires has no published revision; the refusal is 409
documents_not_current and names the documents.
What this makes possible
The overview endpoint composes all of it into one answer: the project's progress, health and freshness; the seven stages with their dates; whether the current stage's gate is approved; the packages; the next five milestones; the latest update and the current baseline; the top risks; and the counts of open decisions, pending approvals and linked documents.
Every figure there is a count or a computed percentage with the list it came from one click away in the console. "62% complete, at risk, three tasks late, update current" is a sentence somebody can act on. "Going well" is not.