Skip to content

[dashboards] Wiki surface in dashboard-pro - #57

Merged
alanshurafa merged 4 commits into
mainfrom
contrib/alanshurafa/wiki-dashboard
Jul 5, 2026
Merged

[dashboards] Wiki surface in dashboard-pro#57
alanshurafa merged 4 commits into
mainfrom
contrib/alanshurafa/wiki-dashboard

Conversation

@alanshurafa

Copy link
Copy Markdown
Owner

Contribution Type

  • Dashboard (/dashboards)

What does this do?

Adds the wiki surface to Dashboard Pro: /wiki (page list with kind filters, section counts, new-page form) and /wiki/[slug] (sections rendered as sanitized markdown with per-section edit, lock/unlock, evidence chips, and the pending-draft review panel — machine proposes, human decides). Six app/api/wiki/* route handlers proxy mutations to the REST gateway with requireSession() before body parse; wikiAvailable() probes at login and caches session.wikiEnabled beside the CRM flag. Pages are URL-reachable; nav entries land in the follow-up default-visible-nav PR.

Merge order: after #56 (needs the /wiki gateway routes at runtime; gate checks are independent).

Requirements

  • schemas/wiki-pages (+ [schemas] Add wiki_reject_pending RPC to wiki-pages #55 for reject-pending) and the [integrations] Add /wiki routes to open-brain-rest #56 gateway routes. Without them, /wiki renders a minimal setup notice instead of erroring.
  • One new dependency: react-markdown@10 with no rehype-raw — embedded HTML renders as text and the default urlTransform strips javascript:/data: link protocols. Verified with a 23-payload static-render XSS suite (script/img/svg injection, protocol variants, reference links, autolinks, title-attribute breakout).

Review pass (fixes included)

  • Evidence chips render as inert short-id chips (full UUID on hover) instead of linking to /thoughts/{id}: that route currently parses ids with parseInt, which truncates digit-leading UUIDs to a small integer and would silently open the wrong thought. Linking returns once the thoughts route accepts UUIDs.
  • Accept/Reject now branch on the response action: a concurrent resolution elsewhere (no_pending) surfaces "This draft was already resolved elsewhere — refreshed." instead of masquerading as success.
  • Archived pages stay editable (parity with wiki_write_section and wiki-mcp, which have no page-status checks) — archived is a list-visibility state, not a freeze; there is no unarchive route yet.
  • Agent-controlled generation_source strings are control/bidi-stripped and length-capped before rendering next to the Accept/Reject decision.

Manual verification

npm run lint 0 errors; npm run build compiles with all routes present; runtime smoke via dev server: unauthenticated mutations 401 before body parse, pages redirect to /login; XSS suite as above; sanitizer behavior test (bidi/control stripping, 80-char cap, non-ASCII preserved). Live-brain browser E2E lands with the integration pass once the gateway PRs are deployed.

Checklist

  • I've read CONTRIBUTING.md
  • My contribution has a README.md with prerequisites, step-by-step instructions, and expected outcome
  • My metadata.json has all required fields
  • If my contribution depends on a skill or primitive, I declared it in metadata.json and linked it in the README
  • I tested this on my own Open Brain instance (static gates + runtime smoke; live-brain E2E pending gateway deployment)
  • No credentials, API keys, or secrets are included

🤖 Generated with Claude Code

alanshurafa and others added 4 commits July 4, 2026 00:19
The wiki surface needs a typed client and feature-detection before any UI
can render. Mirror the CRM pattern so the two optional surfaces stay
symmetric and a brain without the wiki schema degrades the same way.

- lib/types.ts: WikiPage/WikiSection and response types with keys matching
  the /wiki REST contract literally, so the UI can't drift from the gateway.
- lib/api.ts: wikiAvailable() probes GET /wiki/pages (never throws, like
  crmAvailable) plus typed wrappers for every /wiki route. Page slugs are
  arbitrary user text, so they are percent-encoded into every path segment.
- lib/auth.ts: cache wikiEnabled on the session; tolerate undefined on
  cookies minted before the field existed.
- app/login: probe the wiki surface once beside the crm probe, never fatal.
- Add react-markdown (renderer arrives in a later commit).
The browser must never hold the brain key, so every wiki mutation proxies
through a server route that injects x-brain-key. Each handler runs
requireSession() BEFORE parsing the body (house rule) so an unauthed
request gets 401, not a 400 from validation, and never reaches body parse.

One handler per mutation: page create (POST), section write (PUT), and
per-section accept-pending / reject-pending / lock plus page archive
(DELETE). Inputs are validated to the contract (allowed page_kind, boolean
locked, integer display_order, non-empty required strings) so bad values
fail fast here instead of as a 500 from a DB constraint. Upstream error
bodies are logged server-side and never forwarded to the client — only a
safe generic message and the passed-through status code.
The wiki's whole point is regeneration that never stomps human edits, so the
UI has to make section ownership and the machine-vs-human handshake legible.

- /wiki: page list with kind chips, section counts, pagination (the
  /contacts idiom), and a "new page" form. When the schema is absent —
  wikiEnabled===false or GET /wiki/pages 404s — it renders a one-paragraph
  inline notice instead of an error. A later PR swaps this for a shared
  setup-state component, so it is kept minimal and self-contained.
- /wiki/[slug]: sections in display order, each a panel with an origin chip
  (yours / generated), lock toggle, evidence chip linking supporting
  thoughts, inline edit (which takes ownership), and archive with confirm.
  When a machine writer proposes an update to a human-owned section, an
  amber review panel above the body diffs current vs proposed with
  accept / reject — mirroring the CRM proposal accept/reject flow.
- MarkdownBody: section bodies are agent-writable over MCP, so they are
  untrusted. Rendered with react-markdown and NO rehype-raw, so embedded
  HTML is shown as text and never executed — the XSS boundary.
- README: document the optional wiki surface and its degraded empty-state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adversarial review follow-ups (FIX-FIRST verdict):

- Evidence chips are inert text, not links: /thoughts/[id] parseInt()s
  its param, so a digit-leading UUID silently opens an unrelated numeric
  thought (parseInt('3fa85f64-...') === 3). Mirrors FieldEvidence's inert
  rendering; linking awaits a UUID-capable thought route. Full UUID shown
  on hover; README claim updated to match.
- Accept/Reject race: call() now returns the parsed body and the handlers
  branch on action. 'no_pending' (draft resolved in another tab or by
  another agent) surfaces a distinct notice alongside the refresh instead
  of pretending this click decided it. Errors from lock and accept/reject
  now render in a header strip so they are visible even while a pending
  panel is present (previously gated behind !hasPending).
- Removed the UI-only freeze on archived pages: the gateway deliberately
  allows section writes regardless of page status (wiki_write_section has
  no page-status check), and freezing created a one-way door of frozen
  pending drafts and unlockable locks with no unarchive route. Archive
  button stays active-only; badge and banner stay. README notes archived
  pages remain fetchable and editable by slug.
- generationSummary() sanitizes agent-controlled labels before display
  (strip control + bidi chars, trim, 80-char cap) since they render
  beside the Accept/Reject decision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@alanshurafa
alanshurafa merged commit 316beac into main Jul 5, 2026
3 checks passed
@alanshurafa
alanshurafa deleted the contrib/alanshurafa/wiki-dashboard branch July 5, 2026 21:32
@alanshurafa
alanshurafa restored the contrib/alanshurafa/wiki-dashboard branch July 5, 2026 21:35
@alanshurafa
alanshurafa deleted the contrib/alanshurafa/wiki-dashboard branch July 5, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant