Human-readable deployment duration output; release v1.3.2 - #46
Conversation
Add Format-TierModelDuration public cmdlet that renders deploy console durations as <1ms / Xms / Xs / Xm Ys (floor-based, never zero). Route the nine Deploy-TierModel.ps1 console duration sites through it; log lines stay raw milliseconds. Floor-based minute arithmetic avoids the [int][TimeSpan]::TotalMinutes banker's-rounding defect. Bump module version 1.3.1 -> 1.3.2 and update the version-pinned manifest and module tests. Add Unit.FormatDuration.Tests.ps1 (23 cases incl. minute rounding regression guards), wiring-proof integration assertions, and manifest verb-allowlist + export coverage. Refresh README test metrics. Lab-validated on a live DC (2s / 74ms / 1m 47s). Closes #34
There was a problem hiding this comment.
Pull request overview
This PR introduces a reusable duration-formatting primitive to make Deploy-TierModel.ps1 console Duration: output human-readable (ms / s / m+s) while keeping machine-parsable logs in raw milliseconds, and bumps the module release to v1.3.2.
Changes:
- Added a new public cmdlet
Format-TierModelDurationand exported it from the module manifest. - Updated all console
Duration:outputs inDeploy-TierModel.ps1to use the new formatter (leavingWrite-TierModelLogduration strings unchanged). - Updated module versioning + added/updated unit and integration tests, plus release documentation (README/CHANGELOG).
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Unit.ModuleManifest.Tests.ps1 | Updates version assertion, allows Format-* verb, and asserts Format-TierModelDuration is exported. |
| tests/Unit.FormatDuration.Tests.ps1 | Adds dedicated unit coverage for the 4-tier duration formatting contract. |
| tests/Integration.Module.Tests.ps1 | Updates version assertion for the v1.3.2 release. |
| tests/Integration.Deploy.Tests.ps1 | Updates deploy output assertions and adds wiring-proof coverage for the minute-tier display. |
| README.md | Refreshes reported test counts and coverage metrics to reflect the new test run. |
| modules/TierModel/TierModel.psd1 | Bumps module version to 1.3.2 and exports Format-TierModelDuration. |
| modules/TierModel/public/Format-TierModelDuration.ps1 | Adds the new duration formatting cmdlet implementation. |
| Deploy-TierModel.ps1 | Routes console Duration: output through Format-TierModelDuration at all call sites. |
| CHANGELOG.md | Adds the v1.3.2 release notes section describing the duration formatting change and related items. |
| .squad/agents/wolverine/history.md | Updates agent history with the completed test deliverables and measured metrics. |
| .squad/agents/beast/history.md | Updates agent history with the design/implementation notes for the duration formatting work. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| .PARAMETER Milliseconds | ||
| Duration in milliseconds as a [double]. Accepts both [int] and [double] | ||
| DurationMs values returned by module cmdlets. Expected input domain is | ||
| finite, non-negative milliseconds. | ||
|
|
||
| .OUTPUTS | ||
| [string] Human-readable duration string. | ||
|
|
||
| .NOTES | ||
| Expected input domain is finite, non-negative milliseconds. |
|
|
||
| # Sub-1ms floor — covers 0, negatives, and sub-ms values. | ||
| # Must run before any [int] cast so 0.4ms cannot truncate to 0 and render as "0ms". | ||
| if ($Milliseconds -lt 1.0) { return '<1ms' } |
Refresh the measured-coverage header (1,652 tests, 88.93%, 2026-08-24), add a v1.3.2 callout documenting Format-TierModelDuration (100%, 11/11) and the new Unit.FormatDuration.Tests.ps1, and list the new file in the coverage tier tables. The authoritative overall (88.93%, 15,057/16,932 commands) matches the README; the per-file line-count snapshot is noted as pending a separate full refresh.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
.squad/agents/beast/history.md:61
- This session note says the module version stayed at 1.3.1, but this PR bumps the manifest/module tests to 1.3.2. Since this is meant to be a historical record, it should reflect the actual version change in the same PR.
### IMPLEMENTATION COMPLETE (2026-08-24) — production code only, no tests
- `modules/TierModel/public/Format-TierModelDuration.ps1` — CREATED (4 tiers, all floor-based, parse clean)
- `modules/TierModel/TierModel.psd1` — `'Format-TierModelDuration'` added to FunctionsToExport after `'Copy-TierModelAdmx'`
- `Deploy-TierModel.ps1` — 9 Write-Host sites updated; L559/L701 log lines left raw
- `CHANGELOG.md` — Unreleased entry added (issue #34)
- 12/12 smoke-test cases passed including the two banker's-rounding regression cases (90000ms→1m 30s, 119999ms→1m 59s)
- ModuleVersion stays 1.3.1 (not bumped per Joel instruction)
- Tests: NOT written — Wolverine phase after lab validation
modules/TierModel/public/Format-TierModelDuration.ps1:26
- Comment-based help says the expected input domain is "finite, non-negative" milliseconds, but the implementation explicitly treats negative values as a valid case (returns '<1ms'). This is a documentation inconsistency that can confuse callers about whether negative durations are supported or rejected.
.PARAMETER Milliseconds
Duration in milliseconds as a [double]. Accepts both [int] and [double]
DurationMs values returned by module cmdlets. Expected input domain is
finite, non-negative milliseconds.
.OUTPUTS
[string] Human-readable duration string.
.NOTES
Expected input domain is finite, non-negative milliseconds.
| | `modules/TierModel/public/Write-TierModelLog.ps1` | 37 | 0 | 37 | ✅ 100% | | ||
| | **TOTAL (68 files)** | 11,342 | 1,518 | 12,860 | **85.96%** | | ||
| | `modules/TierModel/public/Format-TierModelDuration.ps1` | 11 | 0 | 11 | ✅ 100% | | ||
| | **TOTAL (69 files)** | 11,353 | 1,518 | 12,871 | **88.21%** | | ||
|
|
||
| > **✦** = File has a documented structural barrier preventing full coverage without production code refactoring. See **Hard Coverage Limits** table below for details. | ||
|
|
||
| > **Docs-scope coverage (#41 — 2026-08-18): 88.98% aggregate** (Pester command coverage, full suite 1,627 tests). `Audit-TierModel.ps1` **77.16%**: ByServer live-LDAP paths (`Invoke-CanonicalAclAudit` ByServer-mode canonical check and error paths) are exempt from the coverage requirement per team ruling — same precedent as `Test-TierModelCanonicalAcl.ps1` ByServer branch (92.86%). All module-scope files meet the 80% CI gate. | ||
| > **Docs-scope coverage (v1.3.2 — 2026-08-24): 88.93% aggregate** (Pester command coverage, full suite 1,652 tests; 15,057/16,932 commands — the authoritative overall figure, consistent with the README). The per-file line-count table above is the 2026-08-18 (#41) snapshot plus the new `Format-TierModelDuration.ps1`; a full per-file refresh is tracked separately. `Audit-TierModel.ps1` **77.16%**: ByServer live-LDAP paths (`Invoke-CanonicalAclAudit` ByServer-mode canonical check and error paths) are exempt from the coverage requirement per team ruling — same precedent as `Test-TierModelCanonicalAcl.ps1` ByServer branch (92.86%). All module-scope files meet the 80% CI gate. |
Summary
Renders deployment duration in human-readable form instead of raw milliseconds (issue #34). Adds a
Format-TierModelDurationpublic cmdlet and routes the nineDeploy-TierModel.ps1consoleDuration:sites through it.Output tiers (floor-based, never zero):
0/< 1ms<1ms1–999 msXms1–59 sXs>= 60 sXm YsLog lines (
Write-TierModelLog) keep raw milliseconds for machine parsing.Design notes
[double](not[long]) — realDurationMsvalues are doubles (e.g.2254.1343); a[long]cast truncates0.4ms -> 0ms.< 1msgate guarantees a non-zero display for any input (an idempotent single-object create is sub-second) — never0m 0s.[int][TimeSpan]::TotalMinutes, which banker's-rounds (90000mswould wrongly show2m 30s) and is non-monotonic.Release
[1.3.2]CHANGELOG section also captures the two changes already onmainsince the 1.3.1 re-release: the Best Practices / AD Hardening documentation page (Add Manager attribute on privileged accounts for delegated group population #33) and the green canonical-overlap remediation message.Testing
tests/Unit.FormatDuration.Tests.ps1— 23 cases across all four tiers, including90000/119999/120000banker's-rounding regression guards.DurationMs 2254 -> "Duration: 2s", all-converged no-op ->"Duration: <1ms", aggregate140000 -> "Duration: 2m 20s".-OuOnly -ConfirmApply->Duration: 2s; delete 1 OU + re-run ->Duration: 74ms;-FullDeployment -ConfirmApply->Duration: 1m 47s.Closes #34