Skip to content

Docs Hub sprints 1+2 closure + sprint 2 build (Hub landing, viewer overhaul)#57

Merged
cdarnell merged 9 commits into
mainfrom
qukaizen/arail-docs-hub-sprint-2-bundle
May 17, 2026
Merged

Docs Hub sprints 1+2 closure + sprint 2 build (Hub landing, viewer overhaul)#57
cdarnell merged 9 commits into
mainfrom
qukaizen/arail-docs-hub-sprint-2-bundle

Conversation

@cdarnell
Copy link
Copy Markdown
Owner

Summary

Two sprints bundled for v0.5 release prep:

docs-hub-sprint-1 — closure

  • REVIEW: PASS, QA: PASS (22 new edge-case tests, 1 LOW finding S1)
  • S1 fix applied: _DOCS_DENYLIST composed with _ROOT_DENYLIST at module load so root-denied filenames (CLAUDE.md / AGENTS.md / README.md / CODE_OF_CONDUCT.md) can never register as user-facing docs even if dropped into docs/.

docs-hub-sprint-2 — Phase B + C

  • NEW src/arail/portal/templates/docs_hub.html — Hub landing replaces the /docs → /docs/INDEX.md redirect. Hero, featured strip, category sections, client-side search filter, all driven from docs_registry.by_category().
  • REWRITE src/arail/portal/templates/doc_viewer.html — 3-column viewer with siblings (left rail), TOC H2/H3 (right rail), and footer strip (prev/next + related + "Ask Buddy about this" CTA).
  • Tier filter at the render boundary (_filter_by_tier) applied on both hub and viewer — architect-audience docs hidden on min tier; tier-blocked viewer passes doc=None so the title never leaks.
  • RENAME docs/design.md → docs/portal-design.md (resolves slug collision with root design.md; atomic with denylist removal — F11).
  • REVIEW: PASS, QA: WEAK_PASS (47 new QA tests; 0 FAIL; product correctness clean in isolation; 5 cross-suite flakes traced to a pre-existing del sys.modules pattern in sprint-1 QA — documented as Sprint 3 carry-over).

Test totals

  • Combined sprint surface (registry + registry_qa + routes + routes_qa): 125/125 in isolation
  • New tests this PR: 47 QA + S1 pin-test flip
  • Full suite: 1432 passed; 13 reported failures, all pre-existing / known isolation flakes (none touch sprint code)

Test plan

  • pytest tests/test_docs_registry.py tests/test_docs_routes.py tests/test_docs_registry_qa.py tests/test_docs_routes_qa.py → 125/125 in isolation
  • Manual: /docs renders the new Hub (not the old INDEX.md redirect)
  • Manual: open any /docs/<slug> — siblings, TOC, related, "Ask Buddy" CTA all render
  • Manual: on min tier, architect-audience docs are hidden from Hub and return a tier-block page from viewer
  • Manual: docs/portal-design.md accessible at /docs/portal-design; root design.md still resolves at /docs/design

Sprint 3 carry-overs

  • Fix del sys.modules pattern in tests/test_docs_registry_qa.py:41 (use monkeypatch.setattr to rebind arail.portal.app._docs_registry instead).
  • Convert TOC injection from sequential body_html.replace(..., 1) to a token-walking renderer if exotic markdown lands.
  • LanceDB ingest of docs/; full cross-link audit; delete docs/INDEX.md.

🤖 Generated with Claude Code

cdarnell and others added 9 commits May 16, 2026 19:32
Adds tests/test_docs_registry_qa.py covering the gaps in the builder's
36 sprint tests, weighted per ARCHITECTURE.md §8 (30% security /
25% regression / 20% happy / 15% setup / 10% Buddy voice).

Security (9): Jinja/HTML/JS payloads stored as opaque data; non-string
tags coerced; path-traversal/URL/null-byte in related: never opens
files; dict-shape related: handled; BOM/CRLF + non-UTF8 + 2 MB doc;
concurrent readers under invalidation.

Regression (4): nav order Knowledge < Docs < Agents pinned; unknown
LAB_TIER doesn't 500; denylist effective despite valid frontmatter;
in-place mtime edit invalidates cache (addresses REVIEW W2).

Happy (3): new-file pickup, sibling boundaries, empty related: falls
back to tag overlap.

Setup (3): side-effect-free import, python-frontmatter>=1.1.0
satisfied, portal app imports clean.

Buddy voice (1): three populated buddy_prompts checked for "Pip"
collision and generic-AI phrasing.

56/56 sprint tests green. No defects found.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y into Hub + viewer

Sprint 2 of the Docs Hub effort. Phase B (Hub landing replacing the
/docs redirect) + Phase C (3-column viewer with TOC, siblings, related,
Ask Buddy CTA stub) + two Sprint-1 carry-overs (tier filter at render
boundary, rename docs/design.md to resolve slug collision).

Scope ceiling ~700 LOC across ~6 files. Top failure modes: tier leak
(architect doc on min Hub), path-traversal guard preservation, slug-
collision regression on the rename.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ug collision)

- git mv docs/design.md -> docs/portal-design.md; update frontmatter title
- Remove "design.md" from _DOCS_DENYLIST in docs_registry.py (F11 atomic)
- Add GET /docs/design.md -> 301 /docs/portal-design.md redirect in app.py (F10)
- Import docs_registry in app.py (used in Steps 2-3)
- Add test_legacy_design_redirect (F10) and test_no_slug_collision_after_rename (F11)
- All 38 tests pass (31 registry + 7 routes)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace RedirectResponse at /docs with docs_hub() handler
- Add _filter_by_tier, _featured_docs, _recently_updated helpers in app.py
- New templates/docs_hub.html: hero + search filter + featured strip +
  recently-updated chips + category sections + footer; Jinja autoescape
  prevents XSS on card titles (F8)
- Tests 1-8 from §6.1 all pass (hub 200, min/max tier, empty registry
  fallback, featured strip, search input, XSS escaping)
- Total: 45 tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… Buddy CTA

- Add _slugify() and _render_with_toc() helpers in app.py:
  extracts H2/H3 headings with stable IDs (deduped with numeric suffix, F6)
  degrades to toc=[] on any parse error (F5)
- Widen serve_local_doc() handler:
  audience gate (F15): architect docs return upgrade-hint panel on min tier,
  title not revealed in response
  registry context: doc, toc, siblings_prev/next, related, buddy_prompt_url
  (F9: URL-encoded via urllib.parse.quote_plus)
- Rewrite doc_viewer.html: 3-column grid (left rail / center article / right TOC)
  collapses to single column below 900px; preserves .doc-shell styles for center;
  footer strip: prev/next chips, related cards, Ask Buddy CTA stub (Sprint 3 wires
  chat-side seed consumption — TODO comment in template)
  degrades gracefully when doc=None (INDEX.md, no registry entry, F2)
- Tests 9-17, 20, 21 from §6.1 all pass
- Total: 56 tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…der docs/ too

Closes the LOW-severity S1 finding from docs-hub-sprint-1 QA:
CLAUDE.md / AGENTS.md / README.md / CODE_OF_CONDUCT.md were in
_ROOT_DENYLIST but not _DOCS_DENYLIST, so dropping any of them into
docs/ would register them as user-facing docs.

Fix: compose _DOCS_DENYLIST with _ROOT_DENYLIST at module load. One
line, visible-at-source. Existing pin test (test_root_denylist_files_in_docs_dir_dont_leak)
flipped from "pin current leak" to "assert no leak".

Also pins Sprint 1 REVIEW.md + SPRINT.md final state (PASS verdicts).

Tests: 78/78 (registry + qa + routes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… new QA tests

QA findings: 0 FAIL, 1 INFO (pre-existing sys.modules pattern in
sprint-1 QA file — Sprint 3 carry-over). Product correctness clean
in isolation; cross-suite flakes are test-infra only.

Coverage: XSS×21 (escape verified, SSTI canary not evaluated),
path-traversal×5 (incl. URL-encoded variants), tier title-leak
sentinel, concurrent registry/hub reads, 100-doc stress, unicode
TOC headings, Buddy URL encoding (5KB + newlines + special chars).

Closes docs-hub-sprint-2. Ready to ship bundled with sprint-1 S1
carry-over fix in PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cdarnell cdarnell merged commit cc16b7e into main May 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant