You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(registry): version scope + snapshot content source — the #61 foundation (phases 1-3) (#67)
* feat(config): DocVersion value object + the versions config surface
## Summary
Phase 1 of #61: `DocsKit::DocVersion` (Data.define + `.from` normalizer,
mirroring TopbarLink) and the config knobs multi-version docs hang off —
`c.versions`, `c.repo_url`, `c.snapshots_path`, plus `current_version`,
`version(id)`, `resolve_version(id)`, `versioning_enabled?`, and
`compare_url(from, to)`. `noindex` defaults to the inverse of `current`;
`versioning_enabled?` needs two versions; everything defaults so an
unconfigured site is byte-identical to before.
## Test Coverage
- doc_version_spec: normalization (both key styles), label/noindex
defaulting, path_prefix, current?/archived?
- configuration_spec: default [] + versioning_enabled? false (the
backwards-compat pin), current_version fallback order, compare_url nil
on missing repo_url/refs
## Verification
- [x] bundle exec rubocop passes
- [x] bundle exec rspec passes
Refs #61
* feat(controller): request-scoped DocsKit::Scope + controller wiring
## Summary
Phase 2 of #61: the ONE request-scoped seam (`DocsKit::Scope.with(version:)`,
Thread.current-backed, Rails-free, with the reserved :locale slot for i18n M2).
`DocsKit::Controller#render_page` wraps its render in the scope resolved from
`params[:version]` (unknown ids fall back to current — degrade, never 500);
the gem's own llms/search/mcp controllers get the same scope via the new
`DocsKit::Scoping` around_action module. Unversioned sites scope to nil:
today's behavior exactly.
## Test Coverage
- scope_spec: empty default, ensure-restore (incl. on raise), nesting,
no leaks across sequential calls, path_prefix
- scoping_spec: around_action registration + params → scope resolution
- controller_spec: render runs inside the resolved scope; unknown param
falls back to current; scope restored after; nil on unversioned sites
- the three controller source-wiring specs assert `include DocsKit::Scoping`
## Verification
- [x] bundle exec rubocop passes
- [x] bundle exec rspec passes
Refs #61
* feat(registry): snapshot content source + version-aware enumeration
## Summary
Phase 3 of #61: `DocsKit::Snapshot` reads a committed Markdown snapshot back
as the registry duck type (#all/#from_slug/#nav_items, memoized per version +
manifest-mtime invalidated; missing/unreadable manifest degrades to an EMPTY
snapshot, never raises). `Snapshot::Entry#view_class` is the new minimal
`DocsUI::ArchivedPage` (frozen Markdown through today's chrome; the banner
lands with phase 4). `Registry::Entry#renderable` + the one
`LlmsText.renderable_for` shim make live and snapshot pages render
identically at the three call sites (llms, search, MCP).
`LlmsText.pages(config, version:)` resolves nil → Scope → current, so every
AI surface follows the request's version; `Configuration#nav_groups` derives
an archived sidebar from the snapshot (hrefs already prefixed — the
Sidebar's strict path match works for free).
## Test Coverage
- snapshot_spec: manifest order/grouping, version-prefixed hrefs, mtime
re-read, empty-snapshot degradation, markdown_for, the duck type
- registry_spec: Entry#renderable (nil when unauthored)
- llms_text_spec: renderable_for shim (legacy fallback), pages per
version/scope + the unversioned backwards-compat pin
- configuration_spec: nav_groups under archived/current/no scope
- archived_page_spec: markdown body + masthead via the #body seam
## Verification
- [x] bundle exec rubocop passes
- [x] bundle exec rspec passes (896 examples; 94.3% line coverage)
Refs #61
* ci: break the blocks.rb render chain one call per line
CI's freshly-resolved RuboCop and the locally-pinned 1.88.2 disagree on
which receiver `.reject` must align with in the old two-line form (the
gem root's Gemfile.lock is gitignored, so CI floats to the newest cop).
One call per line, all dots aligned, satisfies both readings of
Layout/MultilineMethodCallIndentation.
Refs #61
0 commit comments