Skip to content

Known Issues

This page lists defects and limitations in what TruePPM already ships. It is the companion to What TruePPM Doesn’t Do Yet, which covers capabilities that were never built. The split is worth keeping straight:

  • Doesn’t do yet — resource leveling, cost and earned value, a mobile app. Absent by plan.
  • Known issues (this page) — something that exists but is wrong, incomplete, or slower than it should be.

Every entry names the issue tracking it and the release it is fixed in. When an issue closes, its entry comes off this page.

The deterministic single-project CPM pass is the most mature part of TruePPM and the part we are most confident in. These are its edges.

Monte Carlo runs on the request thread — planned for 0.5

Section titled “Monte Carlo runs on the request thread — planned for 0.5”

A Monte Carlo simulation executes inline in the HTTP request cycle, gated only by a per-user rate-limit scope. On a large project at a high run count this occupies a web worker for the duration of the simulation.

  • Impact: a slow simulation can crowd out other requests on a small deployment.
  • Workaround: lower the run count; the P50/P80/P95 converge well before the default 1 000 runs on most networks.
  • Fix planned for 0.5 — #2273 moves it into Celery.

Program-scoped Monte Carlo is not exposed — planned for 0.5

Section titled “Program-scoped Monte Carlo is not exposed — planned for 0.5”

The program schedule view computes a program-true critical path across member projects, but only deterministically. There is no program-scoped Monte Carlo endpoint and no MCP tool for it, so the one scope where a confidence date matters most is the scope that cannot produce one.

The engine restriction that made this impossible lifts in 0.4 (#1385 — monte_carlo() will honor per-task calendars, see ADR-0672). What remains after that is the API surface, which is why this entry is separate.

  • Impact: a program manager gets a deterministic finish date with no band around it. Ask for a what-if at program scope over MCP and the tool cannot answer.
  • Workaround: run Monte Carlo per member project and reason about the combination manually — this understates joint risk and is not a substitute.
  • Fix planned for 0.5 — #2467.

The risk register does not affect the forecast — planned for 0.5

Section titled “The risk register does not affect the forecast — planned for 0.5”

The risk register and Monte Carlo are separate systems that do not exchange information. A risk’s severity is the product of two 1–5 ordinals; a simulated finish date is computed from three-point estimates and team velocity. Nothing connects them.

The register’s probability × impact score therefore says nothing about the schedule. A high-severity risk sitting on a task with weeks of total float moves the finish date not at all, while a low-severity risk on the critical path may be the single largest driver of the P80 — and the register ranks them the other way around. Linking a risk to a task (which the register supports) records the relationship for a human to read; it does not reach the engine.

  • Impact: P80 does not reflect known risks. The register cannot answer “what would mitigating this buy me?”, so mitigation work completes without the forecast moving.
  • Workaround: widen the pessimistic value of the three-point estimate on the affected task. This is a genuine workaround with genuine costs, and it is worth knowing what they are: a 40%-likely 10-day event is not a wider spread but a second peak, and PERT-Beta cannot represent one — it spreads probability across a gap the real distribution does not have. The padding is also unattributable (nothing records which risk it was for, so it is never removed when the risk closes) and uncorrelated (one risk affecting five tasks becomes five independent spreads, which understates the tail rather than overstating it).
  • Fix planned for 0.5, per ADR-0711: risks become first-class simulation inputs, with one shared draw per risk per iteration across every task it touches. The design closed as #2556 (that issue tracked the ADR, not the build — verified against main for #3628: engine.py still has no RiskDriver concept). The implementation has no open tracking issue as of this writing; filing one is follow-up from #3628.
  • Cost impact is a separate axis and lands later. ADR-0711 treats schedule and cost as the two axes a simulation can answer. TruePPM has no cost data model yet. 0.5 plans a labor cost model (#754) whose Monte Carlo will report a P80 cost beside the P80 date. That cost comes from duration uncertainty priced at resource rates, not from risk-register impacts, which stay schedule-only. Non-labor direct costs follow later (#2557). Risk impact on scope, quality, safety, and compliance is recorded but deliberately never simulated or folded into a combined score.

Velocity-driven P80/P95 is a lower bound — planned for 0.6

Section titled “Velocity-driven P80/P95 is a lower bound — planned for 0.6”

For sprint-delivered work sampled from team velocity, each run’s sprint horizon is clamped. Runs that have not burned down by that horizon are clamped to it rather than sampled further, which truncates the slow right tail.

The weekly-throughput sampler used by a continuous-flow board clamps its horizon the same way, so the same caveat applies to the throughput forecast.

  • Impact: for a team with high throughput variance, the agile P80/P95 is optimistic — a loose lower bound rather than an unbiased estimate. The bias runs in the unhelpful direction.
  • Workaround: treat a velocity-driven P95 as a floor. Three-point (PERT) estimates are not affected.
  • Surfaced in the product: every affected number carries a “a floor, not a percentile” qualifier and a help icon explaining the clamp — on the backlog forecast, the sprint release-horizon chips, and the board’s flow analytics card. See Interpreting results.
  • Fix planned for 0.6 — #2469.

TruePPM ships two coordinated CPM implementations: the Python library (trueppm-scheduler, the authority) and a Rust engine compiled to WebAssembly. They are not yet at parity, and the gap is wider than the architecture diagram suggests.

The WASM engine is not wired into the browser — planned for 0.5

Section titled “The WASM engine is not wired into the browser — planned for 0.5”

The Rust engine ships as a CI-validated conformance reference. The browser’s drag-preview path still uses a TypeScript CPM worker that approximates the calendar as a fixed Mon–Fri week.

  • Impact: the sub-100 ms in-browser drag preview is calendar-approximate. The authoritative server CPM reconciles exact dates on commit, so a committed schedule is always correct — but a preview can differ from what lands.
  • Fix planned for 0.5 — #1777 wires the WASM engine in, or retires the parity claim.

The Rust engine has no Monte Carlo — not scheduled

Section titled “The Rust engine has no Monte Carlo — not scheduled”

The Rust engine implements forward pass, backward pass, floats, and incremental recompute. It has no Monte Carlo at all. Offline and in-browser recompute is therefore deterministic-only; every probabilistic answer requires the server.

Mutation testing does not cover the engine core — planned for 0.5

Section titled “Mutation testing does not cover the engine core — planned for 0.5”

Mutation testing proves the suite’s assertions catch regressions rather than merely executing lines. Its scope is models.py, derive.py, and cli.py. engine.py — the CPM and Monte Carlo core — is out of scope. The score floor within that scope does gate: MUTATION_MIN is 0.92 against an observed 96.8%, and a nightly below it reds.

  • Impact: the strongest available evidence of assertion strength deliberately excludes the code that matters most. Where the roadmap describes “mutation testing on the scheduler”, read it as covering the models/serialization layer, not the engine.
  • Fix planned for 0.5 — #2468.

A foothold, not full WCAG 2.1 AA conformance

Section titled “A foothold, not full WCAG 2.1 AA conformance”

The CI pipeline enforces an axe-core WCAG 2.1 A/AA scan inside the Playwright E2E suite, and a critical or serious violation fails the pipeline — that gate runs today, on every merge, not just in a future release. The 0.4 remediation pass — focus traps across roughly seventy dialogs, drawers, and popovers, 44px touch targets on the board and schedule surfaces, contrast fixes in both light and dark themes, live-region announcements for route changes and async writes, and keyboard operability on the Gantt, board, and outline — shipped with the 0.4 beta. The axe gate itself is #1685 and #2202 ; the remediation ran across the release rather than under one tracking issue.

What that gate does not prove:

  • moderate axe findings do not block a merge. They are being ratcheted in route by route as each surface’s audit lands, so the enforced floor is narrower than the stated commitment.

  • There has been no formal, end-to-end WCAG 2.1 AA audit of the product. The gate above is automated and partial; a professional audit — including manual screen-reader and keyboard-only passes — has not been run.

  • Impact: an evaluator doing PMO due diligence on accessibility will not find a conformance statement anywhere in the product docs before this page, because there isn’t one to give yet. Treat 0.4 as “actively improving, automated-gate-enforced, and not yet audited” — not as “WCAG 2.1 AA compliant.”

  • Fix planned for 0.9 — the formal audit is a GA-hardening item on the roadmap.

The measured ceilings themselves are covered on What TruePPM Doesn’t Do Yet and in the sizing guide. These are the specific defects behind them.

IssueSymptomFix planned for
#3381Each page of the task list skips the rows before it, and skipping a row still evaluates its per-task subqueries — the last page of a 100 000-task project takes 8.7 s against 0.11 s for the first. This is what sets the ~2 000-task comfort ceiling0.5
#3119The Schedule view loads the whole project before it draws anything — about 2.3 KB of JSON per task, 229 MB at 100 000 tasks0.5
#3830The scheduler refuses a project — or a program with cross-project dependencies, which recalculates as one — whose task durations sum past 366 000 days, about 73 000 tasks at a 5-day average0.5
#3831Schedule recalculation holds about 30 KB of memory per task, so the chart’s 2 GiB Celery worker limit is exceeded near 60 000 tasks in one project or one cross-linked program. Raise resources.limits.memory on the worker before then0.5
#3832The Schedule has never been measured in a browser at scale: heap size, first paint, and interaction latency above a few thousand tasks are unknown0.5
#2340The Kanban board renders every card as a DOM node with no virtualization0.5
#2341A remote task_updated event triggers a full multi-page task-list refetch instead of splicing the changed row0.5
#2346TaskRelationViewSet is unpaginated and returns every relation across all member projects0.5

Filtering matured on the Board first and will reach the other views unevenly. Label filtering lands on the Table/Grid and the Product Backlog with 0.4; the Schedule will not get it, and today no view except the Board can save a filter by name.

IssueSymptomFix planned for
#2443The Schedule is the only task view with no label filter — the Board, Table/Grid, and Product Backlog all get one with 0.4, the Gantt does not0.5
#2444The Schedule has no text search and no owner/status filter — you cannot find a task on the Gantt0.5
#2445Saved views are Board-only; Grid, Schedule, Backlog and My Work cannot save a named filter0.5
#2443Filter state does not survive a view switch, and the vocabulary differs per view0.5
#2446My Work has no project filter, so a PM running several projects gets one undifferentiated list0.5

Baseline comparison is a table, not a Gantt overlay — planned for 0.5

Section titled “Baseline comparison is a table, not a Gantt overlay — planned for 0.5”

0.4 brings baseline capture, management, and comparison into the app, but the comparison is a text table in the task drawer. The planned-vs-current ghost-bar overlay on the Gantt is deliberately not in this release — ADR-0376 defers it to 0.5 in its own Consequences section.

  • Impact: you can see that a task slipped and by how much, but you cannot see the slip drawn against the plan on the timeline, which is the reading most people expect from the word “baseline”.
  • Workaround: none — use the drawer’s comparison table.
  • Fix planned for 0.5, per ADR-0376’s own Consequences section (above). The legend previously carried a stray “Planned baseline” swatch with no matching draw call behind it — that was #2696 (closed, fixed): the legend no longer renders it.

A task with only an assignee contributes no load to any capacity view

Section titled “A task with only an assignee contributes no load to any capacity view”

There are two ways to record who is working on a task, and only one of them creates capacity load. Setting a task’s assignee names the person responsible and drives the “can edit their own tasks” permission rule. Adding a resource assignment — a person plus the fraction of their time the task takes — is what the heatmap, the utilization card, overallocation warnings and resource contention all sum.

A task that has an assignee but no resource assignment is therefore real, visible, and owned, while counting as zero load for the person it is assigned to. Nothing in the product currently says so.

  • Impact: a resource manager reading the heatmap can see someone as available while they carry assigned work. The numbers are internally consistent — they sum exactly what they say they sum — but they will disagree with what a team is actually doing if assignments were made the simpler way.
  • Workaround: add a resource assignment (with units) to any task whose load should appear in capacity views, not only an assignee. A task that has both is counted once, from the assignment.
  • Tracked on #3605, which will rule on whether the two paths are reconciled, disclosed in the product, or collapsed.

The team-level agent opt-out exists at project scope only

Section titled “The team-level agent opt-out exists at project scope only”

The instance → workspace → program → project cascade that decides whether agents may read a scope is real and correctly enforced, and the mcp_enabled field is already writable at all three scopes over the API. The UI to set it exists only at Project settings → Agents; there is no Agents section on program or workspace settings yet.

  • Impact: an operator following the runbook to “Program settings → Agents” or the workspace equivalent finds nothing there.
  • Workaround: set project-scope opt-outs in the UI; workspace and program scope are settable through the API (PATCH /api/v1/workspace/ or PATCH /api/v1/programs/{id}/), and the instance-wide kill switch is the TRUEPPM_MCP_ENABLED environment variable (which is enforced first, ahead of every other check).
  • Tracked on #2700 (building the workspace- and program-scope settings sections).

A computed answer does not name the engine that produced it

Section titled “A computed answer does not name the engine that produced it”

Every value the MCP read tools return is computed by the scheduling engine rather than guessed, and the primary answer tools carry a why block explaining the derivation. What they do not carry is the provenance envelope ADR-0112 §2 specifies — the stamp naming which engine version produced the number, inline on the answer itself.

  • Impact: an operator cannot cite an engine version alongside a forecast, or reproduce an answer later against the version that generated it. For an agent workflow that records what it was told, the answer is attributable to a point in time but not to a build.
  • Workaround: the engine version is captured in the agent-action audit log, so the pairing is recoverable after the fact — query GET /api/v1/agent-actions/ and match on the action’s timestamp.
  • Tracked on #2642, currently sequenced for 0.5. The related refusal-transparency gap closed with #2689 : a refused agent request now carries its refusal envelope — verdict, reason and constraint — to the MCP client, per ADR-0809.

Imported assignees match the whole resource catalog, not project membership — planned for 0.5

Section titled “Imported assignees match the whole resource catalog, not project membership — planned for 0.5”

When a CSV, Excel, MS Project, or Jira import reads an assignee column, it matches each value against the workspace-wide resource catalog by display name, and creates a new resource for anything it does not match. It does not check whether that person is a member of the destination project, and it does not accept a UUID, email, or username as an identifier.

Two consequences:

  • Name collisions bind to the wrong person. A cell reading A. Rivera attaches to whichever resource in the catalog carries that name, even one used only on projects you have no access to. Nothing tells you it happened.
  • The catalog grows from cell text. Every unmatched value becomes a new resource, so a column of inconsistent spellings leaves a trail of near-duplicates behind.

This is not a disclosure vector. The resource catalog is already readable by any authenticated user by design, so an import reaches nothing you could not already list; and an assignment is not project membership, so no permission is granted by one. The defect is in what gets written — mis-attributed work and a polluted catalog — not in what can be read.

  • Impact: assignments can land on the wrong person’s record, and the resource catalog accumulates duplicates that someone has to merge by hand.
  • Workaround: before importing, check that the names in your assignee column match the resources you intend, and review Resources afterward for near-duplicates. On a first import into a fresh workspace neither problem can arise.
  • Fix planned for 0.5 — #2485 moves resolution onto a membership-scoped index that accepts UUID, email, or username, and reports an unmatched value instead of inventing a resource for it.

If you hit something that is not on this page, the user menu and the ⌘K palette both have a Report a bug entry that opens the tracker with your version, edition, build SHA, and current route already filled in. It is a link, not a beacon — nothing is transmitted by rendering it, and you can read and edit the context before submitting.


Maintainers: every issue linked from this page carries a comment pointing back here. When you close one, update this page in the same merge request — remove the entry, or move it to the roadmap as shipped.