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,114 @@
# `registry` — `ScopeRegistry`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Owns the shared, conflict-aware registry of plugin-declared scopes
plus the `registerPluginScopes(registry, manifest)` helper the host
calls at install time. The registry's primary job is install-time
hygiene: two plugins claiming the same scope name (`billing:read`
declared by both `billing` and `invoices`) must fail loudly with
`RBAC.SCOPE_CONFLICT` instead of producing whichever-registered-last
behaviour at request time.
The registry is intentionally not the source of truth for the gate
decision. `principal.scopes` is. The registry exists to catch typos
and cross-plugin conflicts at boot, drive the admin UI's
list-of-available-scopes endpoint, and feed the OpenAPI emitter so
a route declaring an undeclared scope raises a startup error.
## Features
- Conflict detection — duplicate `name` across plugins raises
`RBAC.SCOPE_CONFLICT` with the conflicting plugin id in
`error.context.conflictingPlugin`.
- Idempotent re-register — same plugin, same name, same description
is a no-op so hot reload is safe.
- Empty-string scope name rejected with `RBAC.INVALID_SPEC`.
- `unregisterPlugin(plugin)` removes every scope owned by that
plugin — used at uninstall so a future install may legitimately
claim the same name.
- `registerPluginScopes(registry, manifest)` returns the count of
newly-registered (non-idempotent) entries so the host can log a
meaningful summary.
## Usage: Basic
```ts
import {
registerPluginScopes,
ScopeRegistry,
} from "@suaveplan/api-rbac/registry";
const registry = new ScopeRegistry();
registerPluginScopes(registry, {
name: "billing",
scopes: [
{ name: "billing:read", description: "Read invoices" },
{ name: "billing:write" },
],
});
registry.has("billing:read"); // true
registry.list(); // [ … two descriptors … ]
```
## Usage: Advanced
Driving a per-plugin uninstall + reinstall cycle while preserving
ordering invariants:
```ts
import {
registerPluginScopes,
ScopeRegistry,
} from "@suaveplan/api-rbac/registry";
import { RBAC_CODES, RbacError } from "@suaveplan/api-rbac/error-codes";
const registry = new ScopeRegistry();
registerPluginScopes(registry, {
name: "billing",
scopes: [{ name: "billing:read" }],
});
// Cross-plugin claim → conflict.
try {
registerPluginScopes(registry, {
name: "invoices",
scopes: [{ name: "billing:read" }],
});
} catch (err) {
if (err instanceof RbacError) {
if (err.code === RBAC_CODES.SCOPE_CONFLICT) {
// Drop the offending plugin, surface to operator …
}
}
}
// Uninstall the first plugin, then the second may claim it.
registry.unregisterPlugin("billing");
registerPluginScopes(registry, {
name: "invoices",
scopes: [{ name: "billing:read" }],
});
```
## API Reference
- `RegisteredScope` — `{ name, plugin, description? }`.
- `ScopeRegistry` — class with `register / has / get / list /
unregisterPlugin` methods. Public no-arg constructor.
- `registerPluginScopes(registry, manifest)` — iterates
`manifest.scopes[]`, returns the count of newly-registered entries.
## Implementation Notes
The byte-identical idempotence comparison covers `(plugin, name,
description)`. A change in `description` from the same plugin is
treated as a conflict on purpose — it would otherwise be a silent
admin-visible mutation that bypasses the OpenSpec change discipline.
The internal `Map` is intentionally not exposed; callers walk
`list()` for snapshot semantics so a concurrent install cannot
trip an iterator-invalidation bug.