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,44 @@
# `canonicalJson`
## Purpose
Canonical JSON encoder used by `hashManifest`. Object keys are sorted lexicographically at every depth; array order is preserved; whitespace is omitted; `undefined` properties are dropped. The encoder rejects non-finite numbers, bigints, and cyclic structures so two manifests with the same logical content always produce the same byte stream.
## Features
- Deep key-sort at every object depth
- Cycle detection via `WeakSet`
- Reject non-finite numbers and bigints
- `undefined`-as-value coerces to `null` to match JSON.stringify
## Basic Example
```ts
import { canonicalJson } from "@suaveplan/api-manifest/hash";
canonicalJson({ b: 1, a: 2 }); // '{"a":2,"b":1}'
```
## Advanced Example
```ts
import { canonicalJson } from "@suaveplan/api-manifest/hash";
import { createHash } from "node:crypto";
function etag(value: unknown): string {
return createHash("sha256").update(canonicalJson(value), "utf8").digest("hex");
}
```
## API Reference
- `canonicalJson(value: unknown): string` — throws on cycles, bigints, non-finite numbers, and `typeof` values JSON cannot represent (functions, symbols).
## Implementation Notes
Array order is preserved by design because saga step order is semantically meaningful — reordering steps would change the saga's behaviour, so two manifests with reordered steps must hash differently. `undefined` as a property value collapses to nothing (matching `JSON.stringify`); `undefined` as an array element coerces to `"null"` to keep the array length stable. The encoder is recursive but bounded by the tree depth; the WeakSet catches cycles before stack-overflow becomes a failure mode.
## Cross-package Notes
This module is part of the `@suaveplan/api-manifest` Stage-1 contract package. It sits beneath every Stage-2 consumer (`api-host`, `api-loader`, the admin CLI, the IDE plugin) and above the foundation packages (`@suaveplan/error`, `@suaveplan/types`). The strict-mode top-level rejection of unknown keys means upgrading from manifest v1 to v2 is an OpenSpec-driven event: a new key landing in a consumer manifest is a parse failure rather than a silent drift, which is exactly the contract this stage exists to guarantee.
When the host reloads a plugin without a full restart, every manifest field this module validates is compared against the install-time `manifestHash`. A mismatch triggers a hard rollback via the `RollbackJournal` because the host's invariants (route mounts, registered capabilities, declared scopes) are derived from these exact bytes. Treat the schemas and validators here as the contract surface, not just an input filter — downstream code reads `manifest.foo.bar` and relies on those keys being present, well-typed, and non-undefined in the documented optionality regions.

View File

@@ -0,0 +1,49 @@
# `hashManifest`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Compute `sha256:<hex>` over the canonical-JSON encoding of a manifest. Used by `RollbackJournal` entries and the plugin ledger to detect drift between install and reload — a hash mismatch is a hard failure because the manifest is the host's source of truth for the plugin's declarative facts.
## Features
- SHA-256 via Node's `node:crypto` `createHash`
- Canonical JSON encoding via the sibling `canonicalJson` helper
- Hex-encoded digest with a `sha256:` prefix
- Key-order independence — only value differences alter the hash
## Basic Example
```ts
import { hashManifest } from "@suaveplan/api-manifest/hash";
const h = hashManifest(manifest);
```
## Advanced Example
```ts
import { hashManifest } from "@suaveplan/api-manifest/hash";
import { parseManifest } from "@suaveplan/api-manifest/parse";
function detectDrift(prevHash: string, raw: unknown): boolean {
const next = hashManifest(parseManifest(raw));
return next !== prevHash;
}
```
## API Reference
- `hashManifest(manifest: PluginManifest): string` returning `sha256:<64-hex>`.
## Implementation Notes
The `sha256:` prefix mirrors the multibase-style convention used elsewhere in the crypto stack so callers can identify the algorithm without sniffing the digest length. Using `node:crypto` keeps the dependency surface to zero — the rollback journal lives in the host (server tier) so this is acceptable. If a future browser-tier consumer needs the same hash, a thin SubtleCrypto adapter can be added behind a conditional export.
## Cross-package Notes
This module is part of the `@suaveplan/api-manifest` Stage-1 contract package. It sits beneath every Stage-2 consumer (`api-host`, `api-loader`, the admin CLI, the IDE plugin) and above the foundation packages (`@suaveplan/error`, `@suaveplan/types`). The strict-mode top-level rejection of unknown keys means upgrading from manifest v1 to v2 is an OpenSpec-driven event: a new key landing in a consumer manifest is a parse failure rather than a silent drift, which is exactly the contract this stage exists to guarantee.
When the host reloads a plugin without a full restart, every manifest field this module validates is compared against the install-time `manifestHash`. A mismatch triggers a hard rollback via the `RollbackJournal` because the host's invariants (route mounts, registered capabilities, declared scopes) are derived from these exact bytes. Treat the schemas and validators here as the contract surface, not just an input filter — downstream code reads `manifest.foo.bar` and relies on those keys being present, well-typed, and non-undefined in the documented optionality regions.