Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .squad/agents/beast/history.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,67 @@
# beast — History

## Learnings: Format-Duration Design Investigation (issue #34) (2026-08-24)

**Branch:** N/A — Design proposal only (no code written)
**Status:** ✅ PROPOSAL DELIVERED — awaiting Joel go/no-go before implementation

### Key facts established

**All 9 Write-Host (console) duration call sites in Deploy-TierModel.ps1:**
- L2165: `$totalDuration` — consolidated full-deployment results summary
- L2176: hardcoded `"Duration: 0ms"` — no-actions-needed branch (must also change)
- L2227: `$ouResult.DurationMs` — OuOnly (ConfirmApply-gated)
- L2278: `$groupResult.DurationMs` — GroupOnly (ConfirmApply-gated)
- L2338: `$userResult.DurationMs` — UserOnly (ConfirmApply-gated)
- L2395: `$ouAclResult.DurationMs` — OuAclsOnly (ConfirmApply-gated)
- L2479: `$gpoResult.DurationMs` — GposOnly (ConfirmApply-gated)
- L2570: `$admxResult.DurationMs` — AdmxOnly (always shown when ConfirmApply)
- L2767: `$standaloneTotalDuration` — standalone MSA/gMSA/dMSA/WinLaps/Audit results

**Two Write-TierModelLog (machine log) duration sites — KEEP RAW:**
- L559 (OU deployment), L701 (Group deployment)

**DurationMs type is mixed:**
- `New-TierModelOu.ps1`: casts to `[int]` → integer
- `Copy-TierModelAdmx.ps1`: raw `TotalMilliseconds` → double (e.g., 2254.1343)
- Function must accept `[double]` to handle both

**CI coverage scope (ci.yml L120):**
- `modules/TierModel/*.psm1` AND `modules/TierModel/public/*.ps1`
- Private/internal NOT counted; `Format-TierModelDuration` MUST be public

**No internal/ .ps1 files exist** — no private function mechanism is active

**Existing integration tests that assert on "Xms" format (Integration.Deploy.Tests.ps1):**
- L1901: `$output | Should -Match 'Duration: 150ms'` — DurationMs=150 (sub-1000ms → stays '150ms') ✓
- L2744: `$output | Should -Match 'Duration: 100ms'` — DurationMs=100 (sub-1000ms → stays '100ms') ✓
- Both assertions remain compatible after the change (sub-1000ms stays as Xms)

### Proposed design (pending Joel approval)
- Function name: `Format-TierModelDuration` (follows `Verb-TierModelNoun` convention)
- 4-tier format: `<1ms` / `Xms` / `Xs` / `Xm Ys`
- `[double]$Milliseconds` parameter — handles both int and double DurationMs values
- Open question for Joel: `<1ms` vs `1ms` ceiling; `Xs` middle tier vs just `Xm Ys` for ≥1s

### Lab acceptance validation added (2026-08-24, Joel request)
Three manual DC01 scenarios mapped to branches/call-sites/expected output:
- Scenario 1: `-OuOnly -ConfirmApply` (all OUs) → seconds branch → L2227 → `Duration: Xs`
- Scenario 2: delete 1 OU, re-run → ms branch → L2227 → `Duration: Xms` (THE never-zero acceptance test)
- Scenario 3: `-FullDeployment -ConfirmApply` → m/s branch → L2165 → `Duration: Xm Ys`
- Optional 2b: fully-converged FullDeployment → `<1ms` floor → L2176 → `Duration: <1ms` (tests hardcoded-zero fix)
Pester covers all branches offline/deterministically; lab proves the end-to-end module call chain on live AD.

### 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

---

## Learnings: Granular Per-Right Audit Output (2026-08-14)

**Branch:** `feature/domain-auditing`
Expand Down
41 changes: 41 additions & 0 deletions .squad/agents/wolverine/history.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
# wolverine — History

## Session 2026-08-24 — Format-TierModelDuration tests (issue #34)

**Status:** ✅ COMPLETE

### Deliverables

| File | Action | Description |
|------|--------|-------------|
| `tests/Unit.FormatDuration.Tests.ps1` | **CREATED** | 23 unit tests, 4 Contexts (sub-ms, ms, seconds, minutes), + null-coercion + return-type |
| `tests/Unit.ModuleManifest.Tests.ps1` | **UPDATED** | Added `Format` to approved-verb regex; added `Format-TierModelDuration` accessibility assertion |
| `tests/Integration.Deploy.Tests.ps1` | **UPDATED** | 3a: OuOnly mock 150ms→2254ms → assert 'Duration: 2s'; 3b: all-converged test + 'Duration: <1ms'; 3c: new It block DurationMs=140000 → 'Duration: 2m 20s' |
| `README.md` | **UPDATED** | Metrics refreshed from actual run (see below) |

### Authoritative Measured Numbers (2026-08-24, Pester 5.9.0)

| Metric | Value |
|--------|-------|
| Unit test files | **22** (+1: Unit.FormatDuration.Tests.ps1) |
| Unit tests | **1,338** (+24: 23 new + 1 manifest assertion) |
| Integration test files | **7** (unchanged) |
| Integration tests | **314** (+1: 2m 20s wiring-proof test) |
| Automated total | **1,652** (+25) |
| Manual tests | **335** (authoritative per Joel, unchanged) |
| Grand total | **1,987** |
| PASS | **1,652** |
| FAIL | **0** |
| Overall coverage | **88.93%** (15,057 / 16,932 commands) |
| Production files | **71/71** (Format-TierModelDuration.ps1 adds the 71st) |

All files above 80% CI gate. Zero regressions. ✅

### Banker's-Rounding Guards Rationale
The 90000 / 119999 / 120000 trio locks the contract against a regression to the old `[int][TimeSpan]::TotalMinutes` pattern. 90000ms = 1.5 minutes; banker's-rounding [int]1.5 → 2 (even) → wrong "2m 30s". The three guards prove the floor path is exercised:
- 90000 → '1m 30s' (not '2m 30s')
- 119999 → '1m 59s' (not '2m 59s')
- 120000 → '2m 0s' (minute boundary sanity)

### No commit per owner request.

---

**2026-08-11 — BUG-006 canonical-ACL tests implemented:** Created `tests/Unit.CanonicalAcl.Tests.ps1` (18 tests, ByBytes path, offline fixtures) + 4 gate tests in `Unit.Prerequisites.Tests.ps1`; under Pester 5.9.0 (supported): TOTAL=1457 PASS=1457 FAIL=0; COVERAGE=91.13%; CanonicalAcl.ps1 33/56=58.93% (ByServer branch offline-untestable), Prerequisites.ps1 409/481=85.03%. Reviewed APPROVE. FINALIZATION COMPLETE: PENDING owner code review + PR. No commit (per owner request).

## Session 2026-08-15 (afternoon) — Coverage Refresh @ 100% Pass Rate
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.2] - 2026-08-24

### Added
- **Human-readable deployment duration output (issue #34)**: `Deploy-TierModel.ps1` console duration lines now render in a four-tier format — `<1ms` (sub-millisecond/zero), `Xms` (1–999 ms), `Xs` (1–59 s), `Xm Ys` (60 s+) — instead of raw milliseconds. A multi-minute full deployment now displays e.g. `Duration: 4m 23s`; a single-OU idempotent run displays e.g. `Duration: 350ms`; a zero-duration no-op displays `Duration: <1ms` (never zero). All tiers use `Math.Floor`; the minute tier uses explicit floor-based integer arithmetic to avoid `[int][TimeSpan]::TotalMinutes` banker's rounding.
- **`Format-TierModelDuration` public cmdlet** (`modules/TierModel/public/Format-TierModelDuration.ps1`): the formatting primitive backing the above change. Accepts `[double]$Milliseconds`; unit-testable in isolation.
- **Best Practices, Governance & AD Hardening documentation page (issue #33)**: new `docs/best-practices.md` covering AD tier model governance, hardening recommendations, and operational best practices; added to mkdocs nav and linked from README.

### Changed
- **Canonical ACL overlap remediation now prints a green `REMEDIATED:` status line per OU** instead of a yellow `WARNING`; the overlap detail (DN + principal) is retained in the result's `Warnings` for audit (`Repair-TierModelCanonicalAcl.ps1`).

## [1.3.1] - 2026-08-18

### Fixed
Expand Down
18 changes: 9 additions & 9 deletions Deploy-TierModel.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2162,7 +2162,7 @@ if ($FullDeployment) {
Write-Host "Applied: $totalApplied" -ForegroundColor Green
Write-Host "Skipped: $totalSkipped" -ForegroundColor Yellow
Write-Host "Errors: $totalErrors" -ForegroundColor $(if ($totalErrors -gt 0) { 'Red' } else { 'Green' })
Write-Host "Duration: $($totalDuration)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $totalDuration)" -ForegroundColor Gray
Write-Host "Converged: $overallConverged" -ForegroundColor $(if ($overallConverged) { 'Green' } else { 'Yellow' })

} elseif ($ConfirmApply -and $deploymentPlan.TotalActions -eq 0) {
Expand All @@ -2173,7 +2173,7 @@ if ($FullDeployment) {
Write-Host "Applied: 0" -ForegroundColor Green
Write-Host "Skipped: 0" -ForegroundColor Yellow
Write-Host "Errors: 0" -ForegroundColor Green
Write-Host "Duration: 0ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration 0)" -ForegroundColor Gray
Write-Host "Converged: True" -ForegroundColor Green
}
}
Expand Down Expand Up @@ -2224,7 +2224,7 @@ else {

# Add duration and convergence info when using ConfirmApply
if ($ConfirmApply -and $ouResult.PSObject.Properties.Name -contains 'DurationMs') {
Write-Host "Duration: $($ouResult.DurationMs)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $ouResult.DurationMs)" -ForegroundColor Gray
Write-Host "Converged: $($ouResult.Converged)" -ForegroundColor $(if ($ouResult.Converged) { 'Green' } else { 'Yellow' })
}
if ($ConfirmApply) {
Expand Down Expand Up @@ -2275,7 +2275,7 @@ else {

# Add duration and convergence info when using ConfirmApply
if ($ConfirmApply -and $groupResult.PSObject.Properties.Name -contains 'DurationMs') {
Write-Host "Duration: $($groupResult.DurationMs)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $groupResult.DurationMs)" -ForegroundColor Gray
Write-Host "Converged: $($groupResult.Converged)" -ForegroundColor $(if ($groupResult.Converged) { 'Green' } else { 'Yellow' })
}
}
Expand Down Expand Up @@ -2335,7 +2335,7 @@ else {

# Add duration and convergence info when using ConfirmApply
if ($ConfirmApply -and $userResult.PSObject.Properties.Name -contains 'DurationMs') {
Write-Host "Duration: $($userResult.DurationMs)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $userResult.DurationMs)" -ForegroundColor Gray
Write-Host "Converged: $($userResult.Converged)" -ForegroundColor $(if ($userResult.Converged) { 'Green' } else { 'Yellow' })
}
}
Expand Down Expand Up @@ -2392,7 +2392,7 @@ else {

# Add duration and convergence info when using ConfirmApply
if ($ConfirmApply -and $ouAclResult.PSObject.Properties.Name -contains 'DurationMs') {
Write-Host "Duration: $($ouAclResult.DurationMs)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $ouAclResult.DurationMs)" -ForegroundColor Gray
Write-Host "Converged: $($ouAclResult.Converged)" -ForegroundColor $(if ($ouAclResult.Converged) { 'Green' } else { 'Yellow' })
}
}
Expand Down Expand Up @@ -2476,7 +2476,7 @@ else {

# Add duration and convergence info when using ConfirmApply
if ($ConfirmApply -and $gpoResult.PSObject.Properties.Name -contains 'DurationMs') {
Write-Host "Duration: $($gpoResult.DurationMs)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $gpoResult.DurationMs)" -ForegroundColor Gray
Write-Host "Converged: $($gpoResult.Converged)" -ForegroundColor $(if ($gpoResult.Converged) { 'Green' } else { 'Yellow' })
}

Expand Down Expand Up @@ -2567,7 +2567,7 @@ else {
Write-Host "Create count: $createCount" -ForegroundColor Yellow
Write-Host "Update count: $updateCount" -ForegroundColor Yellow
Write-Host "Low risk: $totalActions" -ForegroundColor Cyan
Write-Host "Duration: $($admxResult.DurationMs)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $admxResult.DurationMs)" -ForegroundColor Gray
Write-Host "Converged: $(if ($admxResult.Summary.Failed -eq 0) { 'True' } else { 'False' })" -ForegroundColor $(if ($admxResult.Summary.Failed -eq 0) { 'Green' } else { 'Yellow' })
} elseif ($admxPlan) {
# Planning mode - show deployment plan
Expand Down Expand Up @@ -2764,7 +2764,7 @@ if ($activeScopeCount -eq 0 -and $activeIncludeCount -gt 0) {
Write-Host "Applied: $standaloneTotalApplied" -ForegroundColor Green
Write-Host "Skipped: $standaloneTotalSkipped" -ForegroundColor Yellow
Write-Host "Errors: $standaloneTotalErrors" -ForegroundColor $(if ($standaloneTotalErrors -gt 0) { 'Red' } else { 'Green' })
Write-Host "Duration: $($standaloneTotalDuration)ms" -ForegroundColor Gray
Write-Host "Duration: $(Format-TierModelDuration $standaloneTotalDuration)" -ForegroundColor Gray
Write-Host "Converged: $standaloneConverged" -ForegroundColor $(if ($standaloneConverged) { 'Green' } else { 'Yellow' })
} elseif ($standaloneTotalErrors -eq 0) {
Write-Host "`n=== Deployment Plan ===" -ForegroundColor Blue
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ To get started with TierModel, please refer to our comprehensive documentation:

## 🧪 Testing & Quality Assurance

**Current Test Status: ✅ 1,627 passing / 0 failures (100%)** *(Last run: 2026-08-18)*
**Current Test Status: ✅ 1,652 passing / 0 failures (100%)** *(Last run: 2026-08-24)*

| Test Suite | Test Files | Test Cases | Status | Coverage |
|------------|-----------|------------|--------|----------|
| **Unit Tests** | 21 files | 1,314 tests | ✅ All pass | **88.98%** |
| **Integration Tests** | 7 files | 313 tests | ✅ All pass | **88.98%** |
| **Unit Tests** | 22 files | 1,338 tests | ✅ All pass | **88.93%** |
| **Integration Tests** | 7 files | 314 tests | ✅ All pass | **88.93%** |
| **Manual Integration Tests** | 1 file | 335 tests | ✅ 100% Pass | **N/A** |
| **Total** | **28 files** | **1,962 tests** | ✅ **100% passing** | **88.98%** |
| **Total** | **29 files** | **1,987 tests** | ✅ **100% passing** | **88.93%** |

### Test Coverage Highlights
- ✅ **70/70** production files have comprehensive test coverage
- ✅ **1,627 / 1,627** automated test cases passing — 0 failures (Pester 5.9.0, 2026-08-18)
- ✅ **88.98%** overall Pester-measured command coverage (all files above 80% CI gate)
- ✅ **71/71** production files have comprehensive test coverage
- ✅ **1,652 / 1,652** automated test cases passing — 0 failures (Pester 5.9.0, 2026-08-24)
- ✅ **88.93%** overall Pester-measured command coverage (all files above 80% CI gate)
- ✅ **New canonical-ACL resilience (#41):** OU deploy uses a phased verify-and-remediate loop — after each disable-inheritance write, `New-TierModelOu` reads back the DACL and auto-sorts if non-canonical (via `Repair-TierModelCanonicalAcl`); lab-validated at 7/7 OUs corrected per deploy under an inherited-Deny condition. New public cmdlet `Repair-TierModelCanonicalAcl` (**95.40%** coverage) available for standalone use. `Audit-TierModel.ps1` now reports Case 1 (non-canonical domain root, deployment blocker) and Case 2 (non-canonical Tier OU, pre-fix artifact) as structured drift findings.
- ✅ **New in v1.3.0:** `-EnableAuditing` domain audit rule (SACL) support — 4 new cmdlets (`Get-TierModelAuditRule` **100%**, `Get-TierModelAuditRuleFd` **97.73%**, `Test-TierModelAuditRule` **98.41%**, `New-TierModelAuditRule` **84.14%**) covered by `Unit.AuditRuleOperations.Tests.ps1` (47 unit tests) plus `-EnableAuditing` integration tests in Audit and Deploy orchestrators; `Audit-TierModel.ps1` **77.16%**, `Deploy-TierModel.ps1` **81.53%**
- ✅ Mock-based testing (no Active Directory connectivity required)
Expand Down
Loading