Files
2026-05-27 23:29:53 +01:00

103 lines
6.1 KiB
Markdown

# Change: enterprise-docs
- **Package**: `@suaveplan/docs`
- **Status**: PROPOSED
- **Design**: ./design.md
- **Spec delta**: ./specs/docs/spec.md
- **Upstream**: `../suaveplan/packages/universal/docs` (1.0.3)
- **Enterprise audit**: `../suaveplan/enterprise/docs.md`
## Why
Genesis is migrating `@suaveplan/docs` from the legacy `packages/universal/docs/`
checkout into the canonical `packages/devtools/docs/docs/` location at enterprise
quality. The upstream shipped a working markdown discovery + graph + hierarchy +
search toolkit (~430 LOC of source plus tests), but the enterprise audit flagged
gaps that block production use:
1. Hand-crafted `Error` paths with no `@suaveplan/error` integration, no stable
error-code registry, and no `code` field for programmatic discrimination.
2. No OpenTelemetry instrumentation — operators cannot measure discovery /
graph-build / search latency, doc-count volume, or error rate.
3. No Zod validation at the `DiscoverOptions` boundary; bad arguments fail with
`glob`'s native error message rather than a typed `DocsError`.
4. Silent failure mode in `discoverDocs`: unreadable files are swallowed with a
bare `catch {}` — there is no way for operators to observe a permission /
IO error rate.
5. The `parseDoc` implementation casts to `any` (`node as any`) to navigate
the mdast tree, defeating type-safety.
6. No `AbortSignal` propagation through `discoverDocs` (the only long-running
async entrypoint), so callers cannot cancel a sweep of a large monorepo.
7. The `DocCategory` union is closed to the upstream six-category
`packages/<tier>/` layout. Genesis has the eighteen-category
`packages/<category>/<plugin>/<pkg>/` layout, so the upstream derivation
produces `"root"` for every genesis package.
The greenfield rewrite addresses every gap while preserving the upstream public
surface (`discoverDocs`, `buildGraph`, `findOrphans`, `buildHierarchy`,
`parseDoc`, `searchDocs`, `DiscoverOptions`, `DocCategory`, `DocFile`,
`DocGraph`, `DocLink`, `DocNode`, `SearchResult`, `OrphanInfo`, `DocHierarchy`)
so downstream consumers continue to compile against the same import paths.
## What changes
- **Add**: `@suaveplan/docs` at version `0.1.0` under
`packages/devtools/docs/docs/` with full enterprise surface — typed errors via
`@suaveplan/error`, OTel counters + histograms, Zod-validated `DiscoverOptions`,
`AbortSignal` propagation, typed mdast traversal via `@types/mdast`,
eighteen-category genesis `DocCategory` union, IO-error surfacing in
`discoverDocs`, and per-feature subpath exports.
- **Add**: error-code namespace `DOCS.*` with eight stable codes via
`defineCodes("DOCS", [...] as const)`: `INVALID_OPTIONS`, `READ_FAILED`,
`PARSE_FAILED`, `GLOB_FAILED`, `ABORTED`, `LINK_RESOLUTION_FAILED`,
`HIERARCHY_CYCLE_LIMIT`, `SEARCH_INVALID_QUERY`.
- **Add**: OTel instruments — counters `docs_discovered_total`,
`docs_parse_error_total`, `docs_io_error_total`, `docs_search_total` and
histograms `docs_discover_duration_ms`, `docs_parse_duration_ms`,
`docs_graph_build_duration_ms`, `docs_search_duration_ms`.
- **Add**: subpath exports per feature module (`./discovery/discover`,
`./parser/parse`, `./graph/build-graph`, `./graph/find-orphans`,
`./hierarchy/build-hierarchy`, `./search/search`, `./errors/errors`,
`./observability/observability`, `./types`).
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/docs` to enumerate.
- No dependents (verified by `bunx turbo ls --filter=...@suaveplan/docs`).
Future doc-site consumers (`api-docs`, `react-docs`) will adopt this package
via follow-up changes once they migrate; no work is required in this proposal.
## Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
| `discoverDocs(options)` | ✅ | ⬆️ | Now Zod-validates options; surfaces IO errors via `DOCS.READ_FAILED` counter; honours `AbortSignal`; emits `docs_discover_duration_ms`. |
| `parseDoc(absPath, relPath, raw)` | ✅ | ⬆️ | Now uses typed mdast `Heading`/`Link` nodes via `@types/mdast`; no `as any`; emits `docs_parse_duration_ms` histogram; counter `docs_parse_error_total` on frontmatter failure. |
| `buildGraph(docs)` | ✅ | ⬆️ | Same shape; emits `docs_graph_build_duration_ms`. Genesis category awareness in orphan filter (root-tier docs still exempt). |
| `findOrphans(graph)` | ✅ | ✅ | Same shape; sort key unchanged. |
| `buildHierarchy(graph, options?)` | ✅ | ⬆️ | Same shape; cycle limit now configurable via `maxDepth` option (default 8 preserved). |
| `searchDocs(docs, query, limit?)` | ✅ | ⬆️ | Same shape; emits `docs_search_total` + `docs_search_duration_ms`. |
| `DocFile`, `DocLink`, `DocGraph`, `DocNode`, `DocHierarchy`, `DiscoverOptions`, `SearchResult`, `OrphanInfo` | ✅ | ✅ | Re-exported from `./types`. |
| `DocCategory` union | ✅ | ⬆️ | Extended from six (upstream tiers) to the eighteen-category genesis vocabulary so genesis packages classify correctly. |
| Typed errors with `DOCS.*` codes | 🚫 | ⬆️ | NEW — `DocsError extends SuaveplanError<DocsErrorCode>`; all error paths use codes; error-code registry exported. |
| OTel counters + histograms | 🚫 | ⬆️ | NEW — eight instruments wired into every public method. |
| Zod-validated options | 🚫 | ⬆️ | NEW — `DiscoverOptionsSchema` enforces `rootDir: string`, `ignore: string[]?`, `includeApps: boolean?`, `signal: AbortSignal?`. |
| `AbortSignal` propagation | 🚫 | ⬆️ | NEW — `discoverDocs` checks `signal.aborted` between glob and parse, and surfaces `DOCS.ABORTED`. |
| Genesis 18-category awareness | 🚫 | ⬆️ | NEW — `deriveCategory` recognises every category in `scripts/folder-reorg/assignment.yml`. |
## Migration
This is a 0.1.0 first landing — no prior genesis consumers. Downstream waves
that adopted the upstream surface from `../suaveplan/packages/universal/docs`
should:
1. Update imports from `@suaveplan/docs` (path stays the same).
2. Treat `DocsError` as the canonical thrown type; switch on `error.code`
against `DOCS_CODES.*` rather than parsing `error.message`.
3. Adopt `AbortSignal` on `discoverDocs` for cancellable scans of large
monorepos.