Skip to content

mppread: import assignments, their resources and baselines from .mpp (#445) - #507

Merged
yeroo merged 9 commits into
mainfrom
issue-445-mpp-import-builds-no-assignment
Sep 28, 2026
Merged

yeroo merged 9 commits into
mainfrom
issue-445-mpp-import-builds-no-assignment

Conversation

@yeroo

@yeroo yeroo commented Sep 28, 2026

Copy link
Copy Markdown
Owner

What and why

Opening an .mpp lost every assignment baseline, because mppread imported no resources or assignments at all. The baselines have nowhere to live without the assignments they belong to (AssignmentBaseline exists only on Assignment, and MSPDI writes it only inside <Assignment>). This PR imports:

  • Resources (TBkndRsc): UID, ID, name, type (work / material / cost), max units.
  • Assignments (TBkndAssn): UID, task UID (including the project summary task, UID 0), resource UID (including -65535, unassigned), units, work, start and finish.
  • Assignment baselines: slots 0 to 10 (Baseline, Baseline1 to Baseline10). Each slot has Start, Finish, Work and Cost.

Save As MSPDI or .yppx then writes the <Baseline> blocks that Project itself exports.

Closes #445

Baseline BCWS/BCWP: Project stores none for assignments

The issue asked for assignment baseline BCWS/BCWP too. The feasibility check showed Project has none:

  • Project's own XML export writes assignment <Baseline> blocks with only Number, Start, Finish, Work and Cost. BCWS and BCWP appear only at the assignment level.
  • Project's COM Assignment object has no BaselineBCWS or BaselineBCWP property.
  • When BCWS/BCWP values were injected into an MSPDI baseline, Project dropped them on reopen and export.

So a .mpp has none to lose. They stay None, and the generator asserts that Project's XML omits them. That assumption was carried over from the MSPDI side (#289). Assignment-level earned value, progress, rates, contours and delays stay with #342.

Which tables are imported and which are refused

  • No resource or assignment table: the file imports task-only, as before.
  • A table in a layout this PR has not validated: task-only import, as on main. This covers MPP9 legacy files, another index entry size, and record shapes not seen before.
  • A corrupt table in the validated layout: the import is refused with a message. This covers offsets or bounds, count mismatches, duplicate UIDs, unknown task or resource references, VarMeta errors, bad payloads and reversed dates.
  • Rows that are skipped: blank (kind-4) and superseded (kind-2) rows, and Project's internal task-0/resource-0 placeholder assignment.

Oracles and tests

  • New generator corpus/tools/gen_mpp_assignment_baseline_cases.py. It drives Microsoft Project over COM and writes .mpp files plus Project's XML (git-ignored) for these cases:

    • work assignments;
    • all eleven baseline slots with distinct values;
    • progress with a status date;
    • material, cost and unassigned assignments;
    • deleted rows.

    For every slot it asserts that the values Project reports over COM match the XML it saved.

  • mppread/tests/oracle_corpus.rs compares, by UID, the imported resources, assignments and every baseline slot with Project's XML: presence, dates, work, and cost and units compared as numbers. It covers the generated cases and the paired corpus cases 18-22, 24 and 25, and does an MSPDI round-trip. The existing checks of scheduled dates against Project's XML stay green.

  • Synthetic unit tests (these run in CI without the corpus):

    • baseline slots 0, 1 and 10;
    • signed costs, large units and the sentinel time;
    • kind-2 and kind-4 rows, and IDs that differ from UIDs;
    • task-only fallback for an unvalidated layout, and refusal on corruption;
    • a lowered FixedMeta or VarMeta count;
    • Var2Data stored once, however many keys point at it.
  • Commands run: cargo test --workspace, the mppread/projcore/yppxy tests with MPP_PAIRED_CORPUS set, cargo clippy --workspace -- -D warnings and cargo fmt --check.

Review record

The plan was agreed with Codex at v3, and there were four revmux rounds.

  • Round 1: six Major findings, all fixed.

    • The 0xFFFF time sentinel was refused.
    • Var2Data was copied once per key, so a hostile file could exhaust memory.
    • Negative baseline costs were refused.
    • Units were capped at 1000.
    • Deleted and blank rows refused the whole file.
    • Assignments on the project summary task refused the file.

    I added two findings of my own. Baseline slots 2 to 9 had been extrapolated; the generator now saves all eleven slots and the test checks each one. Kind-2 assignment rows had been imported as live assignments; they are now skipped.

    Four Minor and one Immaterial finding were fixed: assignment costs spelled 800.00 instead of 800, an error message naming the wrong cause, missing resource-decoder tests, stale crate docs, and a generator that asserted only the XML.

  • Round 2: Minor findings, all fixed.

    • The test that compared schedules with and without assignments could never fail, because CPM never reads resources. It was replaced by checking units and max units against the XML.
    • A table in an unvalidated layout refused the file; it now falls back to task-only import, as agreed in the plan.
    • Assignment start after finish is now refused.
    • Fixtures now use IDs that differ from UIDs and include superseded rows.
    • Docs and comments were corrected.
  • Round 3: Minor findings, fixed. A wrong FixedMeta count had been treated as an unvalidated layout, and a VarMeta count check that was not exact let a lowered count drop entries.

  • Round 4: one Minor finding, fixed and checked by reading the diff. The record-shape rule was duplicated between two index checks; they now share one helper.

Declined: two unreachable defensive error branches in date() and money() (Immaterial, harmless).

Known limit: COM cannot create a truly blank Resource Sheet row, so that case is covered only by the synthetic test.

Follow-up: a .mpp with a deleted resource is refused by the existing calendar decoder. That is also true on main, and a follow-up issue is filed.

🤖 Generated with Claude Code

yeroo and others added 5 commits September 28, 2026 12:42
…445)

Decode TBkndRsc (identity, name, type, max units) and TBkndAssn (identity,
units, work, dates, Baseline..Baseline10 Start/Finish/Work/Cost) against
Project's own XML. Project stores no assignment baseline BCWS/BCWP, so
those stay None. Adds the Project COM generator for the oracle cases.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
… blank rows, task-0 assignments (#445)

Index TBkndRsc/TBkndAssn records by offsets, skipping kind-4 blanks and
kind-2 superseded versions; store Var2Data ranges instead of per-key
copies; share the task cost/work conversions; pin every baseline slot
0..10 with a generated oracle and assert COM against XML.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
…ks leveling inputs (#445)

An unvalidated TBkndRsc/TBkndAssn stride or row shape keeps the task-only
import instead of refusing; corruption inside the validated layout still
refuses. Drop the with/without-resource schedule comparison (CPM never
reads resources) and compare resource max units and assignment units with
Project's XML instead. Refuse an assignment whose start follows its
finish; fixtures now distinguish UID from ID and carry superseded rows.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
A header-only FixedMeta, a shorter inferred stride, or a lowered count
over valid current-layout records is corruption, not an unvalidated
layout. VarMeta must hold exactly its declared entries (checked on every
generated and paired file). The resource name reader moves into rscdecode.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
#445)

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
yeroo and others added 2 commits September 28, 2026 14:10
Resolve project.rs against #161's task-field import; keep both. Align
overalloc's TBkndRsc/TBkndAssn reading with the new decoders (resource
UID at +4, schema stubs, superseded and blank rows, placeholders); add
the empty baseline timephased_data main's model gained. Compare
IgnoreResourceCalendar with Project-exported XML only; MPXJ's
26-task-calendar disagrees with the binary bit.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
… exclusion to one task (#445)

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
@codecov-commenter

codecov-commenter commented Sep 28, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 94.70468% with 52 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mppread/src/assndecode.rs 93.18% 24 Missing ⚠️
mppread/src/overalloc.rs 72.34% 13 Missing ⚠️
mppread/src/tabledecode.rs 97.91% 7 Missing ⚠️
mppread/src/project.rs 93.61% 6 Missing ⚠️
mppread/src/rscdecode.rs 98.64% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Clippy on rustc 1.98 rejects a constant-size chunks_exact.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
@yeroo

yeroo commented Sep 28, 2026

Copy link
Copy Markdown
Owner Author

Not merged automatically: condition 3 failed (merge-check on 69f5035):

ci-failed: test (macos-latest) FAILURE https://github.com/yeroo/docxy/actions/runs/36416128733/job/108907592418
ci-failed: test (ubuntu-latest) FAILURE https://github.com/yeroo/docxy/actions/runs/36416128733/job/108907592399
ci-failed: test (windows-latest) FAILURE https://github.com/yeroo/docxy/actions/runs/36416128733/job/108907592427

Cause: the Clippy step (rustc 1.98.1, all targets) reports this function has too many arguments (8/7) at mppread/tests/oracle_corpus.rs:193 (check_task_fields). The review round-5 fix added the eighth parameter, and local clippy did not lint test targets. The earlier CI failure (chunks_exact in rscdecode.rs) is fixed in 69f5035. The one automatic CI-fix round is used, so this goes to the human.

Proposed fix: pass the PairExclusions (or a small struct holding the exclusion flags) into check_task_fields instead of separate booleans, or add #[allow(clippy::too_many_arguments)] on that test helper. Then run the whole suite, including cargo clippy --workspace --all-targets -- -D warnings.

Local cargo test --workspace on 69f5035: 3466 passed, 0 failed.

Committed by the planner: the implementer pane cannot write .git.

Co-Authored-By: Codex <noreply@openai.com>
@yeroo
yeroo merged commit fa53f74 into main Sep 28, 2026
9 checks passed
@yeroo
yeroo deleted the issue-445-mpp-import-builds-no-assignment branch September 28, 2026 12:43
@yeroo

yeroo commented Sep 28, 2026

Copy link
Copy Markdown
Owner Author

Merged automatically (auto-merge is on for this checkout):

  • Review: five revmux rounds. Every finding was fixed and verified, except two unreachable defensive error branches in date()/money() (Immaterial), which were declined as harmless.
  • CI: two CI-fix rounds. The first fixed chunks_exact → as_chunks. The second, an extra round the human approved, added #[allow(clippy::too_many_arguments)] on the oracle test helper.
  • Suite: the whole suite is green on 8dea632: 3466 tests, and cargo +1.98.0 clippy --workspace --all-targets -- -D warnings is clean.
  • Merge-check: ok, and GitHub CI is green on that head.

Follow-up filed: #508 (minor, review r1). A .mpp with a deleted resource is refused by the existing calendar decoder; this was already true on main.

Scope note: .mpp import now covers assignment baselines 0–10 (Start/Finish/Work/Cost). Project stores no assignment-baseline BCWS/BCWP, so there is nothing of that to import. Assignment-level earned value and progress stay with #342.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MPP import builds no assignment baselines (dates, work, cost, BCWS/BCWP)

2 participants