Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
# utils/sample
## Purpose
The `sample` module covers the two sampling primitives that sit above `shuffle`: unique-without-replacement `sample` for drawing `k` items from `n`, and `weightedChoice` for proportional index selection. Both accept any `Rng` and throw typed `PseudoRandomError`s on invalid input.
These primitives are intentionally small and allocation-frugal: `sample` reuses `shuffle` then slices, so we never allocate auxiliary structures beyond the shuffled copy; `weightedChoice` walks the weights once to compute the total, then once more to find the tipping point.
## Features
- `sample(input, count, rng)` — unique draw without replacement
- `weightedChoice(weights, rng)` — proportional index draw; rejects negative, NaN or all-zero weights
- Both deterministic for equal seeds + equal RNG class
- Strict input validation (positive integer counts, finite non-negative weights)
## Usage: Basic Example
```ts
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
import { sample, weightedChoice } from "@suaveplan/pseudo-random/utils";
const rng = new Mulberry32Rng(1);
const hand = sample(["A", "K", "Q", "J", "10"], 3, rng);
const bucket = weightedChoice([1, 2, 4], rng); // 0, 1 or 2
```
## Usage: Advanced Example
```ts
import { Sfc32Rng } from "@suaveplan/pseudo-random";
import { weightedChoice } from "@suaveplan/pseudo-random/utils";
type Drop = { id: string; weight: number };
const drops: Drop[] = [
{ id: "common", weight: 70 },
{ id: "rare", weight: 25 },
{ id: "epic", weight: 5 },
];
const rng = new Sfc32Rng("loot-table");
const idx = weightedChoice(drops.map((d) => d.weight), rng);
const chosen = drops[idx];
```
## API Reference
- `sample<T>(input, count, rng)` — `PseudoRandomError.ARG.NEGATIVE_COUNT` or `ARG.INVALID` on bad count; `count === 0` returns `[]`.
- `weightedChoice(weights, rng)` — `ARG.EMPTY_ARRAY` on empty input; `ARG.WEIGHTS_INVALID` on negative, NaN or all-zero weights.
## Implementation Notes
`weightedChoice` draws `rng.nextFloat()` (a 32-bit uniform in `[0, 1)`), multiplies by the total weight, and walks the cumulative sum. The final fallback (`return weights.length - 1`) guards against floating-point rounding that would otherwise fail to find a bucket when the target sits exactly on the total.

View File

@@ -0,0 +1,46 @@
# utils/shuffle
## Purpose
The `shuffle` module provides deterministic Fisher-Yates shuffling and single-element `choice` drawn from a user-supplied `Rng`. Because `Rng` is the canonical interface defined in `@suaveplan/types`, these utilities compose with every `SeededRng` implementation in this package as well as with the CSPRNG in `@suaveplan/random`.
Shuffling is a building block for game-card dealing, playlist generation, A/B assignment, and deterministic test data. Isolating it here ensures every downstream package uses the same unbiased Fisher-Yates algorithm rather than re-implementing it (which frequently introduces subtle bias).
## Features
- `shuffle(input, rng)` — non-mutating, returns a new shuffled array
- `shuffleInPlace(arr, rng)` — mutates the argument, returns it
- `choice(input, rng)` — uniformly random element; typed `PseudoRandomError` on empty input
- Draws exactly `n - 1` uint32 values per shuffle — counts are predictable for replay
## Usage: Basic Example
```ts
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
import { shuffle } from "@suaveplan/pseudo-random/utils";
const rng = new Mulberry32Rng(42);
const deck = shuffle(["A", "K", "Q", "J"], rng);
```
## Usage: Advanced Example
```ts
import { Sfc32Rng } from "@suaveplan/pseudo-random";
import { choice, shuffleInPlace } from "@suaveplan/pseudo-random/utils";
const rng = new Sfc32Rng("round-12");
const deck = ["clubs", "hearts", "diamonds", "spades"];
shuffleInPlace(deck, rng);
const first = choice(deck, rng);
```
## API Reference
- `shuffle<T>(input: ReadonlyArray<T>, rng: Rng): T[]` — returns a new array.
- `shuffleInPlace<T>(arr: T[], rng: Rng): T[]` — mutates and returns.
- `choice<T>(input: ReadonlyArray<T>, rng: Rng): T` — throws `PseudoRandomError.ARG.EMPTY_ARRAY` on empty input.
## Implementation Notes
The Fisher-Yates loop walks from `n-1` down to `1`, swapping `arr[i]` with `arr[j]` where `j = rng.nextUint32() % (i+1)`. The modulo introduces a negligible bias when `i+1` does not divide 2^32 evenly (max bias ≈ `n / 2^32`); for any `n` short of 2^32 elements this is statistically undetectable and matches the behaviour every other mainstream library ships. If you need provably unbiased sampling for large `n`, use `@suaveplan/random`'s `randomInt`, which re-draws on biased samples.