docs: migrate DECISIONS.md to house-format ADRs, and retire the review folder (#131)

D001-D021 become ADR 0001-0021 in docs/decisions/, one decision per file.
D017's PESTER_VERSION amendment was a second decision in one entry and becomes
ADR 0022. ADR 0023 records the migration and reverses the lane2-change-plan
ruling that deliberately kept DECISIONS.md until the CI lane work landed.

DECISIONS.md is reduced to a stub with a D-to-ADR redirect table, so the four
released CHANGELOG entries and older issue bodies that cite it degrade to a
redirect rather than a dead reference.

docs/review/ and docs/lane2-change-plan.md are deleted (ADR 0024). Of 91
findings, 83 were resolved and six of the seven still open were already GitHub
issues; F021 was the exception and is now #130.

CLAUDE.md's Key Conventions list gains the two rules it was missing and becomes
the checklist, with the ADRs carrying rationale.
This commit is contained in:
goodolclint-claude[bot]
2026-09-02 14:49:07 +00:00
committed by GitHub
parent c3f051bba5
commit b90791e2bf
38 changed files with 1045 additions and 4517 deletions
@@ -0,0 +1,48 @@
# ADR 0002 — Password parameters must use SecureString
- **Status:** Accepted
- **Date:** 2026-03-22
- **Deciders:** unrecorded; adopted during review scan 2026-03-22
- **Context source:** `docs/review/findings.json` F051
## Context
`Set-PveVmGuestPassword` accepted a plain `string` password parameter, leaving the credential in managed memory indefinitely.
`Connect-PveServer` already took a `PSCredential`, so the module was inconsistent with itself about how sensitive input arrives.
## Decision
Every cmdlet parameter that accepts a password is a `SecureString`, extracted with `Marshal.SecureStringToGlobalAllocUnicode` and freed with `ZeroFreeGlobalAllocUnicode` in a `finally`.
```csharp
[Parameter(Mandatory = true)]
public SecureString Password { get; set; }
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.SecureStringToGlobalAllocUnicode(Password);
string plainText = Marshal.PtrToStringUni(ptr);
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
```
## Rejected alternatives
A plain `string` parameter. It is simpler to write and to test, and it leaves the credential recoverable from a memory dump for the lifetime of the process:
```csharp
[Parameter(Mandatory = true)]
public string Password { get; set; }
```
## Consequences
The service layer below the cmdlet still receives a plain `string` — the conversion happens at the cmdlet boundary, and `ClusterConfigService.JoinCluster` documents that it expects the converted value. The guarantee is about the module's public surface and the window of exposure, not about the credential never existing in managed memory.
A TLS private key is at least as sensitive as a password; anything accepting one is covered by the same rule.