Thank you for considering a contribution. The engine is the core of an active forensic-scheduling consultancy and powers court-filed expert reports. Contributions are welcome — and the bar is high.
-
Every commit must pass 677 unit tests, 0 fail.
npm test -
Every commit must pass 186/186 cross-validation checks against the Python sibling.
npm run crossval
Crossval requires Python 3 and the CPP
cpm.pyPython reference engine. The reference is loaded from the following paths (first match wins):$CPP_PYTHON_REFERENCE_DIR— explicit single-directory override (set this if you have the reference elsewhere).$CPP_PYTHON_REFERENCE_DIRS— colon/semicolon-separated list of directories (use when the reference + its dependencies live in separate folders)../python_reference/— sibling directory inside this repo (recommended for external contributors; clone the CPP_cpp_common/scripts/tree there).- CPP-internal source-tree layout — falls back to
../scripts/../../xer-parser/scriptsfor CPP developers.
Example:
export CPP_PYTHON_REFERENCE_DIR=/path/to/cpm.py/directory npm run crossvalThe Python reference (
cpm.py) is not currently bundled with this engine repo — it lives in the larger CPP suite (_cpp_common). If you cannot obtain it locally, the CI pipeline will run crossval for you on the PR. -
Run
npm run test:allbefore opening a PR.
The engine is used in court. Sloppy contributions ship as evidence. The following rules are enforced by audit:
-
Every new AACE / SCL / FRE / academic citation must have a WebSearch-verified primary-source URL. Add the URL to
docs/citations.mdin the same PR that introduces the citation. -
Any case name must be verified. A case is real if it appears in a court reporter (F.3d, F. Supp., S.C.R., etc.) or a primary-source database (Westlaw, Canlii, BAILII). A case is fabricated if it appears only in scheduling literature with no primary-source link. The CPP forbidden-citation list:
- Emden v. Homer Holdings — does not exist.
- Leopold-Leasco v. United States — does not exist.
- J.A. Jones v. Plumbers & Pipefitters Local 598 (Wash. App. 2000) — does not exist.
PRs that introduce these or similar fabricated cases will be rejected.
- Kelley & Walker (1959) — must be cited as Eastern Joint IRE-AIEE-ACM Computer Conference, NOT Communications of the ACM. (The CACM citation is a common misquotation.)
- Kahn (1962) — must be cited as CACM 5(11):558-562.
- Tarjan (1972) — must be cited as SIAM J. Comput. 1(2):146-160.
User-facing strings (HTML output, DOCX output, log messages) must not be generated by an LLM at runtime. The engine is deterministic; introducing LLM nondeterminism into a forensic deliverable is a Daubert vulnerability.
LLM-generated strings during development are fine. LLM strings embedded in shipped engine output are not.
The following words are not permitted in user-facing strings (HTML, DOCX, narrative output, error messages, log messages):
- "fudge" (the record / the data) — presumes intent.
- "cooked" (schedule / the books) — presumes intent.
- "deposable" — presumes a litigation strategy.
- "fabricated" — presumes a conclusion.
- "after-the-fact" (when applied to baseline edits) — presumes intent.
Internal variable names and implementation comments may use technical language; the user-facing layer is clean. The engine's buildDaubertDisclosure() is a correlation report, not an intent report.
If you bump the version, bump it in both places:
cpm-engine.js— theENGINE_VERSIONconstant near the top.package.json— theversionfield.
Every output manifest reads ENGINE_VERSION and embeds it in the report. A version skew between the engine and package.json is a Daubert vulnerability.
computeTopologyHash() is a stable promise to opposing experts: any party can recompute the hash from the same XER and confirm the engine ran on the same input. This means:
- The canonical input form (sorted predecessors, normalized lag, etc.) must not change without a major version bump.
- If you must change the canonical form (rare), bump to the next major version (3.x) and document the migration in
CHANGELOG.md.
-
npm testpasses (677+ tests, 0 fail). -
npm run crossvalpasses (186/186 checks). - If you added a new citation, the URL is in
docs/citations.mdand the case/RP is real. - If you bumped the engine version, both
cpm-engine.jsandpackage.jsonare updated. - If you added a new public API, it is documented in
docs/api.md. - If you added a new test fixture, it is in the matching section of
cpm-engine.test.js(sectioned by API surface). - If you changed user-facing strings, none of the forbidden-language words appear.
- If you changed the topology-hash canonical form, you bumped to 3.x and documented in
CHANGELOG.md.
- 4-space indentation. No tabs.
'use strict';at the top of every file.- Pure functions where possible. State is anchored in
_MCfor the v15.md Monte-Carlo engine; nowhere else. - Comments explain why, not what. The forward-pass formula tables in the file header are the gold standard.
- Performance-critical paths get a comment explaining the optimization (see
_walkFromMonfor an example).
Open an issue at https://github.com/danafitkowski/cpp-cpm-engine/issues.
A good bug report includes:
- The minimal
activities+relationshipsarray that reproduces the issue. - The expected output (e.g., from P6 native, or from a hand-computation).
- The observed output (from
computeCPM). - Engine version (
E.ENGINE_VERSION). - Node version (
node --version).
Forensic correctness bugs (silent wrong-answer paths, fabricated citations, missing manifest fields, version skews) are treated as critical. We will publish a fix and a CHANGELOG entry. Performance regressions are handled in the next minor release.
By contributing, you agree your contribution will be licensed under the MIT license that covers the project.
Be technically rigorous. Cite primary sources. Verify before asserting. Be courteous in code review. The engine is read by judges, opposing experts, and academics — its quality reflects on everyone who contributes.